2026-01-29 22:25:33 +08:00

2924 lines
70 KiB
C

// SPDX-License-Identifier: GPL-2.0
/* 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/device.h>
#include <linux/module.h>
#include <linux/io-mapping.h>
#include <linux/interrupt.h>
#include <linux/inetdevice.h>
#include <net/addrconf.h>
#include <linux/time.h>
#include <linux/timex.h>
#include <linux/rtc.h>
#include <linux/aer.h>
#include <linux/debugfs.h>
#include "ossl_knl.h"
#include "hinic_hw_mgmt.h"
#include "hinic_hw.h"
#include "hinic_lld.h"
#include "hinic_pci_id_tbl.h"
#include "hinic_nic_dev.h"
#include "hinic_sriov.h"
#include "hinic_dbgtool_knl.h"
#include "hinic_nictool.h"
#define HINIC_PCI_CFG_REG_BAR 0
#define HINIC_PCI_INTR_REG_BAR 2
#define HINIC_PCI_DB_BAR 4
#define HINIC_PCI_VENDOR_ID 0x19e5
#define SELF_TEST_BAR_ADDR_OFFSET 0x883c
#define HINIC_SECOND_BASE 1000
#define HINIC_SYNC_YEAR_OFFSET 1900
#define HINIC_SYNC_MONTH_OFFSET 1
#define HINIC_MINUTE_BASE 60
#define HINIC_WAIT_TOOL_CNT_TIMEOUT 10000
#define HINIC_WAIT_SRIOV_CFG_TIMEOUT 15000
#define HINIC_DRV_DESC "Huawei(R) Intelligent Network Interface Card Driver"
#define HINICVF_DRV_DESC "Huawei(R) Intelligent Virtual Function Network Driver"
MODULE_AUTHOR("Huawei Technologies CO., Ltd");
MODULE_DESCRIPTION(HINIC_DRV_DESC);
MODULE_VERSION(HINIC_DRV_VERSION);
MODULE_LICENSE("GPL");
#ifdef CONFIG_PCI_IOV
static bool disable_vf_load;
module_param(disable_vf_load, bool, 0444);
MODULE_PARM_DESC(disable_vf_load,
"Disable virtual functions probe or not - default is false");
#endif /* CONFIG_PCI_IOV */
enum {
HINIC_FUNC_IN_REMOVE = BIT(0),
HINIC_FUNC_PRB_ERR = BIT(1),
HINIC_FUNC_PRB_DELAY = BIT(2),
};
/* Structure pcidev private */
struct hinic_pcidev {
struct pci_dev *pcidev;
void *hwdev;
struct card_node *chip_node;
struct hinic_lld_dev lld_dev;
/* Record the service object address,
* such as hinic_dev and toe_dev, fc_dev
*/
void *uld_dev[SERVICE_T_MAX];
/* Record the service object name */
char uld_dev_name[SERVICE_T_MAX][IFNAMSIZ];
/* It is a the global variable for driver to manage
* all function device linked list
*/
struct list_head node;
void __iomem *cfg_reg_base;
void __iomem *intr_reg_base;
u64 db_base_phy;
void __iomem *db_base;
#if defined(__aarch64__)
void __iomem *dwqe_mapping;
#else
struct io_mapping *dwqe_mapping;
#endif
/* lock for attach/detach uld */
struct mutex pdev_mutex;
struct hinic_sriov_info sriov_info;
u32 init_state;
/* setted when uld driver processing event */
unsigned long state;
struct pci_device_id id;
unsigned long flag;
struct work_struct slave_nic_work;
struct workqueue_struct *slave_nic_init_workq;
struct delayed_work slave_nic_init_dwork;
enum hinic_chip_mode chip_mode;
bool nic_cur_enable;
bool nic_des_enable;
struct timer_list syncfw_time_timer;
int card_id;
};
#define HINIC_EVENT_PROCESS_TIMEOUT 10000
#define FIND_BIT(num, n) (((num) & (1UL << (n))) ? 1 : 0)
#define SET_BIT(num, n) ((num) | (1UL << (n)))
#define CLEAR_BIT(num, n) ((num) & (~(1UL << (n))))
#define MAX_CARD_ID 64
static u64 card_bit_map;
LIST_HEAD(g_hinic_chip_list);
struct hinic_uld_info g_uld_info[SERVICE_T_MAX];
static const char *s_uld_name[SERVICE_T_MAX] = {
"nic", "ovs", "roce", "toe", "iwarp", "fc", "fcoe", "migrate"};
enum hinic_lld_status {
HINIC_NODE_CHANGE = BIT(0),
};
struct hinic_lld_lock {
/* lock for chip list */
struct mutex lld_mutex;
unsigned long status;
atomic_t dev_ref_cnt;
};
static struct hinic_lld_lock g_lld_lock;
#define WAIT_LLD_DEV_HOLD_TIMEOUT (10 * 60 * 1000) /* 10minutes */
#define WAIT_LLD_DEV_NODE_CHANGED (10 * 60 * 1000) /* 10minutes */
#define WAIT_LLD_DEV_REF_CNT_EMPTY (2 * 60 * 1000) /* 2minutes */
/* node in chip_node will changed, tools or driver can't get node
* during this situation
*/
static void lld_lock_chip_node(void)
{
u32 loop_cnt;
mutex_lock(&g_lld_lock.lld_mutex);
loop_cnt = 0;
while (loop_cnt < WAIT_LLD_DEV_NODE_CHANGED) {
if (!test_and_set_bit(HINIC_NODE_CHANGE, &g_lld_lock.status))
break;
loop_cnt++;
if (loop_cnt % 10000 == 0)
pr_warn("Wait for lld node change complete for %us\n",
loop_cnt / 1000);
usleep_range(900, 1000);
}
if (loop_cnt == WAIT_LLD_DEV_NODE_CHANGED)
pr_warn("Wait for lld node change complete timeout when try to get lld lock\n");
loop_cnt = 0;
while (loop_cnt < WAIT_LLD_DEV_REF_CNT_EMPTY) {
if (!atomic_read(&g_lld_lock.dev_ref_cnt))
break;
loop_cnt++;
if (loop_cnt % 10000 == 0)
pr_warn("Wait for lld dev unused for %us, reference count: %d\n",
loop_cnt / 1000,
atomic_read(&g_lld_lock.dev_ref_cnt));
usleep_range(900, 1000);
}
if (loop_cnt == WAIT_LLD_DEV_REF_CNT_EMPTY)
pr_warn("Wait for lld dev unused timeout\n");
mutex_unlock(&g_lld_lock.lld_mutex);
}
static void lld_unlock_chip_node(void)
{
clear_bit(HINIC_NODE_CHANGE, &g_lld_lock.status);
}
/* When tools or other drivers want to get node of chip_node, use this function
* to prevent node be freed
*/
static void lld_dev_hold(void)
{
u32 loop_cnt = 0;
/* ensure there have not any chip node in changing */
mutex_lock(&g_lld_lock.lld_mutex);
while (loop_cnt < WAIT_LLD_DEV_HOLD_TIMEOUT) {
if (!test_bit(HINIC_NODE_CHANGE, &g_lld_lock.status))
break;
loop_cnt++;
if (loop_cnt % 10000 == 0)
pr_warn("Wait lld node change complete for %us\n",
loop_cnt / 1000);
usleep_range(900, 1000);
}
if (loop_cnt == WAIT_LLD_DEV_HOLD_TIMEOUT)
pr_warn("Wait lld node change complete timeout when try to hode lld dev\n");
atomic_inc(&g_lld_lock.dev_ref_cnt);
mutex_unlock(&g_lld_lock.lld_mutex);
}
static void lld_dev_put(void)
{
atomic_dec(&g_lld_lock.dev_ref_cnt);
}
static void hinic_lld_lock_init(void)
{
mutex_init(&g_lld_lock.lld_mutex);
atomic_set(&g_lld_lock.dev_ref_cnt, 0);
}
static atomic_t tool_used_cnt;
void hinic_tool_cnt_inc(void)
{
atomic_inc(&tool_used_cnt);
}
void hinic_tool_cnt_dec(void)
{
atomic_dec(&tool_used_cnt);
}
static int attach_uld(struct hinic_pcidev *dev, enum hinic_service_type type,
struct hinic_uld_info *uld_info)
{
void *uld_dev = NULL;
int err;
mutex_lock(&dev->pdev_mutex);
if (dev->init_state < HINIC_INIT_STATE_HWDEV_INITED) {
sdk_err(&dev->pcidev->dev, "SDK init failed, can not attach uld\n");
err = -EFAULT;
goto out_unlock;
}
if (dev->uld_dev[type]) {
sdk_err(&dev->pcidev->dev,
"%s driver has attached to pcie device\n",
s_uld_name[type]);
err = 0;
goto out_unlock;
}
if ((hinic_get_func_mode(dev->hwdev) == FUNC_MOD_NORMAL_HOST) &&
type == SERVICE_T_OVS && !hinic_support_ovs(dev->hwdev, NULL)) {
sdk_warn(&dev->pcidev->dev, "Dev not support %s\n",
s_uld_name[type]);
err = 0;
goto out_unlock;
}
err = uld_info->probe(&dev->lld_dev, &uld_dev, dev->uld_dev_name[type]);
if (err || !uld_dev) {
sdk_err(&dev->pcidev->dev,
"Failed to add object for %s driver to pcie device\n",
s_uld_name[type]);
goto probe_failed;
}
dev->uld_dev[type] = uld_dev;
mutex_unlock(&dev->pdev_mutex);
sdk_info(&dev->pcidev->dev,
"Attach %s driver to pcie device succeed\n", s_uld_name[type]);
return 0;
probe_failed:
out_unlock:
mutex_unlock(&dev->pdev_mutex);
return err;
}
static void detach_uld(struct hinic_pcidev *dev, enum hinic_service_type type)
{
struct hinic_uld_info *uld_info = &g_uld_info[type];
u32 cnt = 0;
mutex_lock(&dev->pdev_mutex);
if (!dev->uld_dev[type]) {
mutex_unlock(&dev->pdev_mutex);
return;
}
while (cnt < HINIC_EVENT_PROCESS_TIMEOUT) {
if (!test_and_set_bit(type, &dev->state))
break;
usleep_range(900, 1000);
cnt++;
}
uld_info->remove(&dev->lld_dev, dev->uld_dev[type]);
dev->uld_dev[type] = NULL;
if (cnt < HINIC_EVENT_PROCESS_TIMEOUT)
clear_bit(type, &dev->state);
sdk_info(&dev->pcidev->dev,
"Detach %s driver from pcie device succeed\n",
s_uld_name[type]);
mutex_unlock(&dev->pdev_mutex);
}
static void attach_ulds(struct hinic_pcidev *dev)
{
enum hinic_service_type type;
for (type = SERVICE_T_OVS; type < SERVICE_T_MAX; type++) {
if (g_uld_info[type].probe)
attach_uld(dev, type, &g_uld_info[type]);
}
}
static void detach_ulds(struct hinic_pcidev *dev)
{
enum hinic_service_type type;
for (type = SERVICE_T_MAX - 1; type > SERVICE_T_NIC; type--) {
if (g_uld_info[type].probe)
detach_uld(dev, type);
}
}
int hinic_register_uld(enum hinic_service_type type,
struct hinic_uld_info *uld_info)
{
struct card_node *chip_node;
struct hinic_pcidev *dev;
if (type >= SERVICE_T_MAX) {
pr_err("Unknown type %d of up layer driver to register\n",
type);
return -EINVAL;
}
if (!uld_info || !uld_info->probe || !uld_info->remove) {
pr_err("Invalid information of %s driver to register\n",
s_uld_name[type]);
return -EINVAL;
}
lld_dev_hold();
if (g_uld_info[type].probe) {
pr_err("%s driver has registered\n", s_uld_name[type]);
lld_dev_put();
return -EINVAL;
}
memcpy(&g_uld_info[type], uld_info, sizeof(*uld_info));
list_for_each_entry(chip_node, &g_hinic_chip_list, node) {
list_for_each_entry(dev, &chip_node->func_list, node) {
if (attach_uld(dev, type, uld_info)) {
sdk_err(&dev->pcidev->dev,
"Attach %s driver to pcie device failed\n",
s_uld_name[type]);
continue;
}
}
}
lld_dev_put();
pr_info("Register %s driver succeed\n", s_uld_name[type]);
return 0;
}
EXPORT_SYMBOL(hinic_register_uld);
void hinic_unregister_uld(enum hinic_service_type type)
{
struct card_node *chip_node;
struct hinic_pcidev *dev;
struct hinic_uld_info *uld_info;
if (type >= SERVICE_T_MAX) {
pr_err("Unknown type %d of up layer driver to unregister\n",
type);
return;
}
lld_dev_hold();
list_for_each_entry(chip_node, &g_hinic_chip_list, node) {
list_for_each_entry(dev, &chip_node->func_list, node) {
detach_uld(dev, type);
}
}
uld_info = &g_uld_info[type];
memset(uld_info, 0, sizeof(*uld_info));
lld_dev_put();
}
EXPORT_SYMBOL(hinic_unregister_uld);
#define HINIC_SYNFW_TIME_PERIOD (60 * 60 * 1000)
static void hinic_syncfw_timer_handler(struct timer_list *t)
{
struct hinic_pcidev *pci_adapter = from_timer(pci_adapter, t,
syncfw_time_timer);
u64 tv_msec;
tv_msec = ktime_to_ms(ktime_get_real());
hinic_sync_time_async(pci_adapter->hwdev, tv_msec);
mod_timer(&pci_adapter->syncfw_time_timer,
jiffies + msecs_to_jiffies(HINIC_SYNFW_TIME_PERIOD));
}
void hinic_init_syncfw_timer(struct hinic_pcidev *pci_adapter)
{
if (hinic_get_func_mode(pci_adapter->hwdev) != FUNC_MOD_NORMAL_HOST ||
hinic_func_type(pci_adapter->hwdev) != TYPE_PPF)
return;
timer_setup(&pci_adapter->syncfw_time_timer,
hinic_syncfw_timer_handler, 0);
pci_adapter->syncfw_time_timer.expires =
jiffies + msecs_to_jiffies(HINIC_SYNFW_TIME_PERIOD);
add_timer(&pci_adapter->syncfw_time_timer);
}
void hinic_destroy_syncfw_timer(struct hinic_pcidev *pci_adapter)
{
if (hinic_get_func_mode(pci_adapter->hwdev) != FUNC_MOD_NORMAL_HOST ||
hinic_func_type(pci_adapter->hwdev) != TYPE_PPF)
return;
del_timer_sync(&pci_adapter->syncfw_time_timer);
}
static void hinic_sync_time_to_fmw(struct hinic_pcidev *pdev_pri)
{
struct tm tm = {0};
u64 tv_msec;
int err;
tv_msec = ktime_to_ms(ktime_get_real());
err = hinic_sync_time(pdev_pri->hwdev, tv_msec);
if (err) {
sdk_err(&pdev_pri->pcidev->dev, "Synchronize UTC time to firmware failed, errno:%d.\n",
err);
} else {
time64_to_tm(tv_msec / MSEC_PER_SEC, 0, &tm);
sdk_info(&pdev_pri->pcidev->dev, "Synchronize UTC time to firmware succeed. UTC time %ld-%02d-%02d %02d:%02d:%02d.\n",
tm.tm_year + HINIC_SYNC_YEAR_OFFSET,
tm.tm_mon + HINIC_SYNC_MONTH_OFFSET,
tm.tm_mday, tm.tm_hour,
tm.tm_min, tm.tm_sec);
}
}
enum hinic_ver_incompat_mode {
/* New driver can't compat with old firmware */
VER_INCOMP_NEW_DRV_OLD_FW,
/* New Firmware can't compat with old driver */
VER_INCOMP_NEW_FW_OLD_DRV,
};
struct hinic_version_incompat {
char *version;
char *advise;
u32 incompat_mode;
};
struct hinic_version_incompat ver_incompat_table[] = {
{
.version = "1.2.2.0",
.advise = "Mechanism of cos changed",
.incompat_mode = BIT(VER_INCOMP_NEW_DRV_OLD_FW),
},
{
.version = "1.2.3.0",
.advise = "Driver get sevice mode from firmware",
.incompat_mode = BIT(VER_INCOMP_NEW_DRV_OLD_FW),
},
};
#define MAX_VER_FIELD_LEN 4
#define MAX_VER_SPLIT_NUM 4
static void __version_split(const char *str, int *split_num,
char rst[][MAX_VER_FIELD_LEN])
{
const char delim = '.';
const char *src;
int cnt = 0;
u16 idx, end, token_len;
idx = 0;
while (idx < strlen(str)) {
for (end = idx; end < strlen(str); end++) {
if (*(str + end) == delim)
break; /* find */
}
if (end != idx) {
token_len = min_t(u16, end - idx,
MAX_VER_FIELD_LEN - 1);
src = str + idx;
memcpy(rst[cnt], src, token_len);
if (++cnt >= MAX_VER_SPLIT_NUM)
break;
}
idx = end + 1; /* skip delim */
}
*split_num = cnt;
}
int hinic_version_cmp(char *ver1, char *ver2)
{
char ver1_split[MAX_VER_SPLIT_NUM][MAX_VER_FIELD_LEN] = { {0} };
char ver2_split[MAX_VER_SPLIT_NUM][MAX_VER_FIELD_LEN] = { {0} };
int split1_num, split2_num;
int ver1_num, ver2_num;
int split, err;
/* To compat older firmware version */
if (ver1[0] == 'B')
return -1;
if (ver2[0] == 'B')
return 1;
__version_split(ver1, &split1_num, ver1_split);
__version_split(ver2, &split2_num, ver2_split);
if (split1_num != MAX_VER_SPLIT_NUM ||
split2_num != MAX_VER_SPLIT_NUM) {
pr_err("Invalid version %s or %s\n", ver1, ver2);
return 0;
}
for (split = 0; split < MAX_VER_SPLIT_NUM; split++) {
err = kstrtoint(ver1_split[split], 0, &ver1_num);
err |= kstrtoint(ver2_split[split], 0, &ver2_num);
if (err) {
pr_err("Failed to parse version: %s, %s\n",
ver1_split[split], ver2_split[split]);
return 0;
}
if (ver1_num > ver2_num)
return 1;
else if (ver1_num < ver2_num)
return -1;
}
return 0;
}
static int __version_mismatch(struct hinic_pcidev *pcidev, char *cur_fw_ver,
char *cur_drv_ver,
struct hinic_version_incompat *ver_incompat,
int start_entry)
{
struct hinic_version_incompat *ver_incmp_tmp;
int fw_ver_comp;
int i, num_entry;
fw_ver_comp = hinic_version_cmp(cur_fw_ver, ver_incompat->version);
if (fw_ver_comp <= 0) {
/* Check if new driver compatible with old fw */
for (i = start_entry; i >= 0; i--) {
ver_incmp_tmp = &ver_incompat_table[i];
if (hinic_version_cmp(cur_fw_ver,
ver_incmp_tmp->version) >= 0)
break; /* Not need to check anymore */
if (ver_incmp_tmp->incompat_mode &
BIT(VER_INCOMP_NEW_DRV_OLD_FW)) {
sdk_err(&pcidev->pcidev->dev,
"Version incompatible: %s, please update firmware to %s, or use %s driver\n",
ver_incmp_tmp->advise,
cur_drv_ver, cur_fw_ver);
return -EINVAL;
}
}
return 0;
}
/* check if old driver compatible with new firmware */
num_entry = (int)sizeof(ver_incompat_table) /
(int)sizeof(ver_incompat_table[0]);
for (i = start_entry + 1; i < num_entry; i++) {
ver_incmp_tmp = &ver_incompat_table[i];
if (hinic_version_cmp(cur_fw_ver, ver_incmp_tmp->version) < 0)
break; /* Not need to check anymore */
if (ver_incmp_tmp->incompat_mode &
BIT(VER_INCOMP_NEW_FW_OLD_DRV)) {
sdk_err(&pcidev->pcidev->dev,
"Version incompatible: %s, please update driver to %s, or use %s firmware\n",
ver_incmp_tmp->advise,
cur_fw_ver, cur_drv_ver);
return -EINVAL;
}
}
return 0;
}
static void hinic_ignore_minor_version(char *version)
{
char ver_split[MAX_VER_SPLIT_NUM][MAX_VER_FIELD_LEN] = { {0} };
int max_ver_len, split_num = 0;
int err;
__version_split(version, &split_num, ver_split);
if (split_num != MAX_VER_SPLIT_NUM)
return;
max_ver_len = (int)strlen(version) + 1;
memset(version, 0, max_ver_len);
err = snprintf(version, max_ver_len, "%s.%s.%s.0",
ver_split[0], ver_split[1], ver_split[2]);
if (err <= 0 || err >= max_ver_len)
pr_err("Failed to snprintf version, function return(%d) and dest_len(%d)\n",
err, max_ver_len);
}
static int hinic_detect_version_compatible(struct hinic_pcidev *pcidev)
{
struct hinic_fw_version fw_ver = { {0} };
struct hinic_version_incompat *ver_incompat;
char drv_ver[MAX_VER_SPLIT_NUM * MAX_VER_FIELD_LEN] = {0};
int idx, num_entry, drv_ver_len;
int ver_mismatch;
int err;
err = hinic_get_fw_version(pcidev->hwdev, &fw_ver);
if (err) {
sdk_err(&pcidev->pcidev->dev,
"Failed to get firmware version\n");
return err;
}
drv_ver_len = min_t(int, (int)sizeof(drv_ver) - 1,
(int)strlen(HINIC_DRV_VERSION));
memcpy(drv_ver, HINIC_DRV_VERSION, drv_ver_len);
sdk_info(&pcidev->pcidev->dev, "Version info: driver %s, firmware %s\n",
drv_ver, fw_ver.mgmt_ver);
hinic_ignore_minor_version(fw_ver.mgmt_ver);
hinic_ignore_minor_version(drv_ver);
ver_mismatch = hinic_version_cmp(drv_ver, fw_ver.mgmt_ver);
if (!ver_mismatch)
return 0;
num_entry = (int)sizeof(ver_incompat_table) /
(int)sizeof(ver_incompat_table[0]);
for (idx = num_entry - 1; idx >= 0; idx--) {
ver_incompat = &ver_incompat_table[idx];
if (hinic_version_cmp(drv_ver, ver_incompat->version) < 0)
continue;
/* Find older verion of driver in table */
return __version_mismatch(pcidev, fw_ver.mgmt_ver, drv_ver,
ver_incompat, idx);
}
return 0;
}
struct mctp_hdr {
u16 resp_code;
u16 reason_code;
u32 manufacture_id;
u8 cmd_rsvd;
u8 major_cmd;
u8 sub_cmd;
u8 spc_field;
};
struct mctp_bdf_info {
struct mctp_hdr hdr; /* spc_field: pf index */
u8 rsvd;
u8 bus;
u8 device;
u8 function;
};
enum mctp_resp_code {
/* COMMAND_COMPLETED = 0, */
/* COMMAND_FAILED = 1, */
/* COMMAND_UNAVALILABLE = 2, */
COMMAND_UNSUPPORTED = 3,
};
static void __mctp_set_hdr(struct mctp_hdr *hdr,
struct hinic_mctp_host_info *mctp_info)
{
u32 manufacture_id = 0x07DB;
hdr->cmd_rsvd = 0;
hdr->major_cmd = mctp_info->major_cmd;
hdr->sub_cmd = mctp_info->sub_cmd;
hdr->manufacture_id = cpu_to_be32(manufacture_id);
hdr->resp_code = cpu_to_be16(hdr->resp_code);
hdr->reason_code = cpu_to_be16(hdr->reason_code);
}
static void __mctp_get_bdf(struct hinic_pcidev *pci_adapter,
struct hinic_mctp_host_info *mctp_info)
{
struct pci_dev *pdev = pci_adapter->pcidev;
struct mctp_bdf_info *bdf_info = mctp_info->data;
bdf_info->bus = pdev->bus->number;
bdf_info->device = (u8)(pdev->devfn >> 3); /* 5bits in devfn */
bdf_info->function = (u8)(pdev->devfn & 0x7); /* 3bits in devfn */
memset(&bdf_info->hdr, 0, sizeof(bdf_info->hdr));
__mctp_set_hdr(&bdf_info->hdr, mctp_info);
bdf_info->hdr.spc_field =
(u8)hinic_global_func_id_hw(pci_adapter->hwdev);
mctp_info->data_len = sizeof(*bdf_info);
}
#define MCTP_MAJOR_CMD_PUBLIC 0x0
#define MCTP_MAJOR_CMD_NIC 0x1
#define MCTP_PUBLIC_SUB_CMD_BDF 0x1
#define MCTP_PUBLIC_SUB_CMD_DRV 0x4
#define MCTP_NIC_SUB_CMD_IP 0x1
static void __mctp_get_host_info(struct hinic_pcidev *dev,
struct hinic_mctp_host_info *mctp_info)
{
struct mctp_hdr *hdr;
switch ((((u16)mctp_info->major_cmd) << 8) | mctp_info->sub_cmd) {
case (MCTP_MAJOR_CMD_PUBLIC << 8 | MCTP_PUBLIC_SUB_CMD_BDF):
__mctp_get_bdf(dev, mctp_info);
break;
default:
hdr = mctp_info->data;
hdr->reason_code = COMMAND_UNSUPPORTED;
__mctp_set_hdr(hdr, mctp_info);
mctp_info->data_len = sizeof(*hdr);
break;
}
}
static bool __is_pcidev_match_chip_name(const char *ifname,
struct hinic_pcidev *dev,
struct card_node *chip_node,
enum func_type type)
{
if (!strncmp(chip_node->chip_name, ifname, IFNAMSIZ)) {
if (type == TYPE_UNKNOWN) {
if (dev->init_state < HINIC_INIT_STATE_HW_PART_INITED)
return false;
} else {
if (dev->init_state < HINIC_INIT_STATE_HW_PART_INITED ||
hinic_func_type(dev->hwdev) != type)
return false;
}
return true;
}
return false;
}
static struct hinic_pcidev *_get_pcidev_by_chip_name(char *ifname,
enum func_type type)
{
struct card_node *chip_node;
struct hinic_pcidev *dev;
lld_dev_hold();
list_for_each_entry(chip_node, &g_hinic_chip_list, node) {
list_for_each_entry(dev, &chip_node->func_list, node) {
if (test_bit(HINIC_FUNC_IN_REMOVE, &dev->flag))
continue;
if (__is_pcidev_match_chip_name(ifname, dev, chip_node,
type)) {
lld_dev_put();
return dev;
}
}
}
lld_dev_put();
return NULL;
}
static struct hinic_pcidev *hinic_get_pcidev_by_chip_name(char *ifname)
{
struct hinic_pcidev *dev, *dev_hw_init;
/* find hw init device first */
dev_hw_init = _get_pcidev_by_chip_name(ifname, TYPE_UNKNOWN);
if (dev_hw_init) {
if (hinic_func_type(dev_hw_init->hwdev) == TYPE_PPF)
return dev_hw_init;
}
dev = _get_pcidev_by_chip_name(ifname, TYPE_PPF);
if (dev) {
if (dev_hw_init && dev_hw_init->init_state >= dev->init_state)
return dev_hw_init;
return dev;
}
dev = _get_pcidev_by_chip_name(ifname, TYPE_PF);
if (dev) {
if (dev_hw_init && dev_hw_init->init_state >= dev->init_state)
return dev_hw_init;
return dev;
}
dev = _get_pcidev_by_chip_name(ifname, TYPE_VF);
if (dev)
return dev;
return NULL;
}
static bool __is_pcidev_match_dev_name(const char *ifname,
struct hinic_pcidev *dev,
enum hinic_service_type type)
{
struct hinic_nic_dev *nic_dev;
enum hinic_service_type i;
if (type == SERVICE_T_MAX) {
for (i = SERVICE_T_OVS; i < SERVICE_T_MAX; i++) {
if (!strncmp(dev->uld_dev_name[i], ifname, IFNAMSIZ))
return true;
}
} else {
if (!strncmp(dev->uld_dev_name[type], ifname, IFNAMSIZ))
return true;
}
nic_dev = dev->uld_dev[SERVICE_T_NIC];
if (nic_dev) {
if (!strncmp(nic_dev->netdev->name, ifname, IFNAMSIZ))
return true;
}
return false;
}
static struct hinic_pcidev *
hinic_get_pcidev_by_dev_name(char *ifname, enum hinic_service_type type)
{
struct card_node *chip_node;
struct hinic_pcidev *dev;
lld_dev_hold();
list_for_each_entry(chip_node, &g_hinic_chip_list, node) {
list_for_each_entry(dev, &chip_node->func_list, node) {
if (test_bit(HINIC_FUNC_IN_REMOVE, &dev->flag))
continue;
if (__is_pcidev_match_dev_name(ifname, dev, type)) {
lld_dev_put();
return dev;
}
}
}
lld_dev_put();
return NULL;
}
static struct hinic_pcidev *hinic_get_pcidev_by_ifname(char *ifname)
{
struct hinic_pcidev *dev;
/* support search hwdev by chip name, net device name,
* or fc device name
*/
/* Find pcidev by chip_name first */
dev = hinic_get_pcidev_by_chip_name(ifname);
if (dev)
return dev;
/* If ifname not a chip name,
* find pcidev by FC name or netdevice name
*/
return hinic_get_pcidev_by_dev_name(ifname, SERVICE_T_MAX);
}
int hinic_get_chip_name_by_hwdev(void *hwdev, char *ifname)
{
struct card_node *chip_node;
struct hinic_pcidev *dev;
if (!hwdev || !ifname)
return -EINVAL;
lld_dev_hold();
list_for_each_entry(chip_node, &g_hinic_chip_list, node) {
list_for_each_entry(dev, &chip_node->func_list, node) {
if (test_bit(HINIC_FUNC_IN_REMOVE, &dev->flag))
continue;
if (dev->hwdev == hwdev) {
strscpy(ifname, chip_node->chip_name,
IFNAMSIZ - 1);
ifname[IFNAMSIZ - 1] = 0;
lld_dev_put();
return 0;
}
}
}
lld_dev_put();
return -ENXIO;
}
EXPORT_SYMBOL(hinic_get_chip_name_by_hwdev);
static struct card_node *hinic_get_chip_node_by_hwdev(const void *hwdev)
{
struct card_node *chip_node = NULL;
struct card_node *node_tmp = NULL;
struct hinic_pcidev *dev;
if (!hwdev)
return NULL;
lld_dev_hold();
list_for_each_entry(node_tmp, &g_hinic_chip_list, node) {
if (!chip_node) {
list_for_each_entry(dev, &node_tmp->func_list, node) {
if (test_bit(HINIC_FUNC_IN_REMOVE, &dev->flag))
continue;
if (dev->hwdev == hwdev) {
chip_node = node_tmp;
break;
}
}
}
}
lld_dev_put();
return chip_node;
}
int hinic_get_pf_uld_array(struct pci_dev *pdev, u32 *dev_cnt, void *array[])
{
struct hinic_pcidev *dev = pci_get_drvdata(pdev);
struct card_node *chip_node;
u32 cnt;
if (!dev || !hinic_support_nic(dev->hwdev, NULL))
return -EINVAL;
lld_dev_hold();
cnt = 0;
chip_node = dev->chip_node;
list_for_each_entry(dev, &chip_node->func_list, node) {
if (test_bit(HINIC_FUNC_IN_REMOVE, &dev->flag))
continue;
if (dev->init_state < HINIC_INIT_STATE_NIC_INITED)
continue;
if (HINIC_FUNC_IS_VF(dev->hwdev))
continue;
array[cnt] = dev->uld_dev[SERVICE_T_NIC];
cnt++;
}
lld_dev_put();
*dev_cnt = cnt;
return 0;
}
int hinic_get_chip_cos_up_map(struct pci_dev *pdev, bool *is_setted, u8 *cos_up)
{
struct hinic_pcidev *dev = pci_get_drvdata(pdev);
struct card_node *chip_node;
if (!dev)
return -EINVAL;
chip_node = dev->chip_node;
*is_setted = chip_node->cos_up_setted;
if (chip_node->cos_up_setted)
memcpy(cos_up, chip_node->cos_up, sizeof(chip_node->cos_up));
return 0;
}
int hinic_set_chip_cos_up_map(struct pci_dev *pdev, u8 *cos_up)
{
struct hinic_pcidev *dev = pci_get_drvdata(pdev);
struct card_node *chip_node;
if (!dev)
return -EINVAL;
chip_node = dev->chip_node;
chip_node->cos_up_setted = true;
memcpy(chip_node->cos_up, cos_up, sizeof(chip_node->cos_up));
return 0;
}
void *hinic_get_hwdev_by_ifname(char *ifname)
{
struct hinic_pcidev *dev;
dev = hinic_get_pcidev_by_ifname(ifname);
if (dev)
return dev->hwdev;
return NULL;
}
void *hinic_get_uld_dev_by_ifname(char *ifname, enum hinic_service_type type)
{
struct hinic_pcidev *dev;
if (type >= SERVICE_T_MAX) {
pr_err("Service type: %d is error\n", type);
return NULL;
}
dev = hinic_get_pcidev_by_dev_name(ifname, type);
if (dev)
return dev->uld_dev[type];
return NULL;
}
void *hinic_get_uld_by_chip_name(char *ifname, enum hinic_service_type type)
{
struct hinic_pcidev *dev;
/* support search hwdev by chip name, net device name,
* or fc device name, Find pcidev by chip_name first
*/
dev = hinic_get_pcidev_by_chip_name(ifname);
if (dev)
return dev->uld_dev[type];
return NULL;
}
/* NOTICE: nictool can't use this function, because this function can't keep
* tool context mutual exclusive with remove context
*/
void *hinic_get_ppf_uld_by_pdev(struct pci_dev *pdev,
enum hinic_service_type type)
{
struct hinic_pcidev *pci_adapter;
struct card_node *chip_node;
struct hinic_pcidev *dev;
if (!pdev)
return NULL;
pci_adapter = pci_get_drvdata(pdev);
if (!pci_adapter)
return NULL;
chip_node = pci_adapter->chip_node;
lld_dev_hold();
list_for_each_entry(dev, &chip_node->func_list, node) {
/* can't test HINIC_FUNC_IN_REMOVE bit in dev->flag, because
* TOE will call this function when detach toe driver
*/
if (hinic_func_type(dev->hwdev) == TYPE_PPF) {
lld_dev_put();
return dev->uld_dev[type];
}
}
lld_dev_put();
return NULL;
}
EXPORT_SYMBOL(hinic_get_ppf_uld_by_pdev);
void *hinic_get_ppf_hwdev_by_pdev(struct pci_dev *pdev)
{
struct hinic_pcidev *pci_adapter;
struct card_node *chip_node;
struct hinic_pcidev *dev;
if (!pdev)
return NULL;
pci_adapter = pci_get_drvdata(pdev);
if (!pci_adapter)
return NULL;
chip_node = pci_adapter->chip_node;
lld_dev_hold();
list_for_each_entry(dev, &chip_node->func_list, node) {
if (test_bit(HINIC_FUNC_IN_REMOVE, &dev->flag) ||
dev->init_state < HINIC_INIT_STATE_HW_IF_INITED)
continue;
if (dev->hwdev && hinic_func_type(dev->hwdev) == TYPE_PPF) {
lld_dev_put();
return dev->hwdev;
}
}
lld_dev_put();
return NULL;
}
void hinic_get_all_chip_id(void *id_info)
{
struct nic_card_id *card_id = (struct nic_card_id *)id_info;
struct card_node *chip_node;
int i = 0;
int id, err;
lld_dev_hold();
list_for_each_entry(chip_node, &g_hinic_chip_list, node) {
err = sscanf(chip_node->chip_name, HINIC_CHIP_NAME "%d", &id);
if (err <= 0)
pr_err("Failed to get hinic id\n");
card_id->id[i] = id;
i++;
}
lld_dev_put();
card_id->num = i;
}
static bool __is_func_valid(struct hinic_pcidev *dev)
{
if (test_bit(HINIC_FUNC_IN_REMOVE, &dev->flag))
return false;
if (dev->init_state < HINIC_INIT_STATE_HWDEV_INITED)
return false;
if (HINIC_FUNC_IS_VF(dev->hwdev))
return false;
return true;
}
bool hinic_is_valid_bar_addr(u64 offset)
{
struct card_node *chip_node = NULL;
struct hinic_pcidev *dev;
lld_dev_hold();
list_for_each_entry(chip_node, &g_hinic_chip_list, node) {
list_for_each_entry(dev, &chip_node->func_list, node) {
if (hinic_func_type(dev->hwdev) == TYPE_VF)
continue;
if (test_bit(HINIC_FUNC_IN_REMOVE, &dev->flag))
continue;
if (offset == pci_resource_start(dev->pcidev, 0)) {
lld_dev_put();
return true;
}
}
}
lld_dev_put();
return false;
}
void hinic_get_card_info(void *hwdev, void *bufin)
{
struct card_node *chip_node = NULL;
struct card_info *info = (struct card_info *)bufin;
struct hinic_nic_dev *nic_dev;
struct hinic_pcidev *dev;
void *fun_hwdev;
u32 i = 0;
info->pf_num = 0;
chip_node = hinic_get_chip_node_by_hwdev(hwdev);
if (!chip_node)
return;
lld_dev_hold();
list_for_each_entry(dev, &chip_node->func_list, node) {
if (!__is_func_valid(dev))
continue;
fun_hwdev = dev->hwdev;
if (((hinic_support_fc(fun_hwdev, NULL)) ||
(hinic_support_fcoe(fun_hwdev, NULL))) &&
dev->uld_dev[SERVICE_T_FC]) {
info->pf[i].pf_type |= (u32)BIT(SERVICE_T_FC);
strscpy(info->pf[i].name,
dev->uld_dev_name[SERVICE_T_FC], IFNAMSIZ);
}
if (hinic_support_nic(fun_hwdev, NULL)) {
nic_dev = dev->uld_dev[SERVICE_T_NIC];
if (nic_dev) {
info->pf[i].pf_type |= (u32)BIT(SERVICE_T_NIC);
strscpy(info->pf[i].name,
nic_dev->netdev->name, IFNAMSIZ);
}
}
if ((hinic_support_ovs(fun_hwdev, NULL)) &&
dev->uld_dev[SERVICE_T_OVS])
info->pf[i].pf_type |= (u32)BIT(SERVICE_T_OVS);
if ((hinic_support_roce(fun_hwdev, NULL)) &&
dev->uld_dev[SERVICE_T_ROCE])
info->pf[i].pf_type |= (u32)BIT(SERVICE_T_ROCE);
if ((hinic_support_toe(fun_hwdev, NULL)) &&
dev->uld_dev[SERVICE_T_TOE])
info->pf[i].pf_type |= (u32)BIT(SERVICE_T_TOE);
if (hinic_func_for_mgmt(fun_hwdev))
strscpy(info->pf[i].name, "FOR_MGMT", IFNAMSIZ);
if (hinic_func_for_pt(fun_hwdev))
info->pf[i].pf_type |= (u32)BIT(SERVICE_T_PT);
if (hinic_func_for_hwpt(fun_hwdev))
info->pf[i].pf_type |= (u32)BIT(SERVICE_T_HWPT);
strscpy(info->pf[i].bus_info, pci_name(dev->pcidev),
sizeof(info->pf[i].bus_info));
info->pf_num++;
i = info->pf_num;
}
lld_dev_put();
}
void hinic_get_card_func_info_by_card_name(const char *chip_name,
struct hinic_card_func_info
*card_func)
{
struct card_node *chip_node = NULL;
struct hinic_pcidev *dev;
struct func_pdev_info *pdev_info;
card_func->num_pf = 0;
lld_dev_hold();
list_for_each_entry(chip_node, &g_hinic_chip_list, node) {
if (strncmp(chip_node->chip_name, chip_name, IFNAMSIZ))
continue;
list_for_each_entry(dev, &chip_node->func_list, node) {
if (hinic_func_type(dev->hwdev) == TYPE_VF)
continue;
if (test_bit(HINIC_FUNC_IN_REMOVE, &dev->flag))
continue;
pdev_info = &card_func->pdev_info[card_func->num_pf];
pdev_info->bar0_size = pci_resource_len(dev->pcidev, 0);
pdev_info->bar0_phy_addr =
pci_resource_start(dev->pcidev, 0);
card_func->num_pf++;
if (card_func->num_pf >= MAX_SIZE)
break;
}
}
lld_dev_put();
}
int hinic_get_device_id(void *hwdev, u16 *dev_id)
{
struct card_node *chip_node = NULL;
struct hinic_pcidev *dev;
u16 vendor_id = 0;
u16 device_id = 0;
chip_node = hinic_get_chip_node_by_hwdev(hwdev);
if (!chip_node)
return -ENODEV;
lld_dev_hold();
list_for_each_entry(dev, &chip_node->func_list, node) {
if (test_bit(HINIC_FUNC_IN_REMOVE, &dev->flag))
continue;
pci_read_config_word(dev->pcidev, 0, &vendor_id);
if (vendor_id == HINIC_PCI_VENDOR_ID) {
pci_read_config_word(dev->pcidev, 2, &device_id);
break;
}
}
lld_dev_put();
*dev_id = device_id;
return 0;
}
int hinic_get_pf_id(void *hwdev, u32 port_id, u32 *pf_id, u32 *isvalid)
{
struct card_node *chip_node = NULL;
struct hinic_pcidev *dev;
chip_node = hinic_get_chip_node_by_hwdev(hwdev);
if (!chip_node)
return -ENODEV;
lld_dev_hold();
list_for_each_entry(dev, &chip_node->func_list, node) {
if (test_bit(HINIC_FUNC_IN_REMOVE, &dev->flag) ||
dev->init_state < HINIC_INIT_STATE_HWDEV_INITED)
continue;
if (hinic_physical_port_id(dev->hwdev) == port_id) {
*pf_id = hinic_global_func_id(dev->hwdev);
*isvalid = 1;
break;
}
}
lld_dev_put();
return 0;
}
void hinic_get_fc_devname(char *devname)
{
struct card_node *chip_node;
struct hinic_pcidev *dev;
lld_dev_hold();
list_for_each_entry(chip_node, &g_hinic_chip_list, node) {
list_for_each_entry(dev, &chip_node->func_list, node) {
if (test_bit(HINIC_FUNC_IN_REMOVE, &dev->flag))
continue;
if (dev->init_state < HINIC_INIT_STATE_NIC_INITED)
continue;
if (HINIC_FUNC_IS_VF(dev->hwdev))
continue;
if (dev->uld_dev[SERVICE_T_FC]) {
strscpy(devname,
dev->uld_dev_name[SERVICE_T_FC],
IFNAMSIZ);
lld_dev_put();
return;
}
}
}
lld_dev_put();
}
enum hinic_init_state hinic_get_init_state(struct pci_dev *pdev)
{
struct hinic_pcidev *dev = pci_get_drvdata(pdev);
if (dev)
return dev->init_state;
return HINIC_INIT_STATE_NONE;
}
enum hinic_init_state hinic_get_init_state_by_ifname(char *ifname)
{
struct hinic_pcidev *dev;
dev = hinic_get_pcidev_by_ifname(ifname);
if (dev)
return dev->init_state;
pr_err("Can not get device %s\n", ifname);
return HINIC_INIT_STATE_NONE;
}
int hinic_get_self_test_result(char *ifname, u32 *result)
{
struct hinic_pcidev *dev = NULL;
dev = hinic_get_pcidev_by_ifname(ifname);
if (!dev) {
pr_err("Get pcidev failed by ifname: %s\n", ifname);
return -EFAULT;
}
*result = be32_to_cpu(readl((u8 __iomem *)(dev->cfg_reg_base) +
SELF_TEST_BAR_ADDR_OFFSET));
return 0;
}
struct net_device *hinic_get_netdev_by_lld(struct hinic_lld_dev *lld_dev)
{
struct hinic_pcidev *pci_adapter;
struct hinic_nic_dev *nic_dev;
if (!lld_dev || !hinic_support_nic(lld_dev->hwdev, NULL))
return NULL;
pci_adapter = pci_get_drvdata(lld_dev->pdev);
nic_dev = pci_adapter->uld_dev[SERVICE_T_NIC];
if (!nic_dev) {
sdk_err(&pci_adapter->pcidev->dev,
"There's no net device attached on the pci device\n");
return NULL;
}
return nic_dev->netdev;
}
EXPORT_SYMBOL(hinic_get_netdev_by_lld);
void *hinic_get_hwdev_by_netdev(struct net_device *netdev)
{
struct hinic_nic_dev *nic_dev = netdev_priv(netdev);
if (!nic_dev || !netdev)
return NULL;
return nic_dev->hwdev;
}
EXPORT_SYMBOL(hinic_get_hwdev_by_netdev);
struct net_device *hinic_get_netdev_by_pcidev(struct pci_dev *pdev)
{
struct hinic_pcidev *pci_adapter;
struct hinic_nic_dev *nic_dev;
if (!pdev)
return NULL;
pci_adapter = pci_get_drvdata(pdev);
if (!pci_adapter || !hinic_support_nic(pci_adapter->hwdev, NULL))
return NULL;
nic_dev = pci_adapter->uld_dev[SERVICE_T_NIC];
if (!nic_dev) {
sdk_err(&pci_adapter->pcidev->dev,
"There`s no net device attached on the pci device\n");
return NULL;
}
return nic_dev->netdev;
}
EXPORT_SYMBOL(hinic_get_netdev_by_pcidev);
struct hinic_sriov_info *hinic_get_sriov_info_by_pcidev(struct pci_dev *pdev)
{
struct hinic_pcidev *pci_adapter = pci_get_drvdata(pdev);
return &pci_adapter->sriov_info;
}
bool hinic_is_in_host(void)
{
struct card_node *chip_node;
struct hinic_pcidev *dev;
lld_dev_hold();
list_for_each_entry(chip_node, &g_hinic_chip_list, node) {
list_for_each_entry(dev, &chip_node->func_list, node) {
if (test_bit(HINIC_FUNC_IN_REMOVE, &dev->flag))
continue;
if (dev->init_state > HINIC_INIT_STATE_PCI_INITED &&
hinic_func_type(dev->hwdev) != TYPE_VF) {
lld_dev_put();
return true;
}
}
}
lld_dev_put();
return false;
}
int hinic_attach_nic(struct hinic_lld_dev *lld_dev)
{
struct hinic_pcidev *dev;
if (!lld_dev)
return -EINVAL;
dev = container_of(lld_dev, struct hinic_pcidev, lld_dev);
return attach_uld(dev, SERVICE_T_NIC, &g_uld_info[SERVICE_T_NIC]);
}
EXPORT_SYMBOL(hinic_attach_nic);
void hinic_detach_nic(struct hinic_lld_dev *lld_dev)
{
struct hinic_pcidev *dev;
if (!lld_dev)
return;
dev = container_of(lld_dev, struct hinic_pcidev, lld_dev);
detach_uld(dev, SERVICE_T_NIC);
}
EXPORT_SYMBOL(hinic_detach_nic);
int hinic_attach_roce(struct hinic_lld_dev *lld_dev)
{
struct hinic_pcidev *dev;
if (!lld_dev)
return -EINVAL;
dev = container_of(lld_dev, struct hinic_pcidev, lld_dev);
return attach_uld(dev, SERVICE_T_ROCE, &g_uld_info[SERVICE_T_ROCE]);
}
EXPORT_SYMBOL(hinic_attach_roce);
void hinic_detach_roce(struct hinic_lld_dev *lld_dev)
{
struct hinic_pcidev *dev;
if (!lld_dev)
return;
dev = container_of(lld_dev, struct hinic_pcidev, lld_dev);
detach_uld(dev, SERVICE_T_ROCE);
}
EXPORT_SYMBOL(hinic_detach_roce);
static int __set_nic_rss_state(struct hinic_pcidev *dev, bool enable)
{
void *nic_uld;
int err = 0;
if (test_bit(HINIC_FUNC_IN_REMOVE, &dev->flag))
return 0;
nic_uld = dev->uld_dev[SERVICE_T_NIC];
if (!hinic_support_nic(dev->hwdev, NULL) || !nic_uld)
return 0;
if (hinic_func_type(dev->hwdev) == TYPE_VF)
return 0;
if (enable)
err = hinic_enable_func_rss(nic_uld);
else
err = hinic_disable_func_rss(nic_uld);
if (err) {
sdk_err(&dev->pcidev->dev, "Failed to %s rss\n",
enable ? "enable" : "disable");
}
return err;
}
int hinic_disable_nic_rss(struct hinic_lld_dev *lld_dev)
{
struct hinic_pcidev *adapter;
if (!lld_dev)
return -EINVAL;
adapter = container_of(lld_dev, struct hinic_pcidev, lld_dev);
return __set_nic_rss_state(adapter, false);
}
EXPORT_SYMBOL(hinic_disable_nic_rss);
int hinic_enable_nic_rss(struct hinic_lld_dev *lld_dev)
{
struct hinic_pcidev *adapter;
if (!lld_dev)
return -EINVAL;
adapter = container_of(lld_dev, struct hinic_pcidev, lld_dev);
return __set_nic_rss_state(adapter, true);
}
EXPORT_SYMBOL(hinic_enable_nic_rss);
struct pci_device_id *hinic_get_pci_device_id(struct pci_dev *pdev)
{
struct hinic_pcidev *adapter;
if (!pdev)
return NULL;
adapter = pci_get_drvdata(pdev);
return &adapter->id;
}
EXPORT_SYMBOL(hinic_get_pci_device_id);
static int __set_nic_func_state(struct hinic_pcidev *pci_adapter)
{
struct pci_dev *pdev = pci_adapter->pcidev;
u16 func_id;
int err;
bool enable_nic;
err = hinic_global_func_id_get(pci_adapter->hwdev, &func_id);
if (err)
return err;
err = hinic_get_func_nic_enable(pci_adapter->hwdev, func_id,
&enable_nic);
if (err) {
sdk_err(&pdev->dev, "Failed to get nic state\n");
return err;
}
if (enable_nic) {
if (is_multi_bm_slave(pci_adapter->hwdev))
hinic_set_vf_dev_cap(pci_adapter->hwdev);
err = attach_uld(pci_adapter, SERVICE_T_NIC,
&g_uld_info[SERVICE_T_NIC]);
if (err) {
sdk_err(&pdev->dev, "Failed to initialize NIC\n");
return err;
}
if (pci_adapter->init_state < HINIC_INIT_STATE_NIC_INITED)
pci_adapter->init_state = HINIC_INIT_STATE_NIC_INITED;
} else {
detach_uld(pci_adapter, SERVICE_T_NIC);
}
return 0;
}
int hinic_ovs_set_vf_nic_state(struct hinic_lld_dev *lld_dev, u16 vf_func_id,
bool en)
{
struct hinic_pcidev *dev, *des_dev;
struct hinic_nic_dev *uld_dev;
int err = -EFAULT;
if (!lld_dev)
return -EINVAL;
dev = pci_get_drvdata(lld_dev->pdev);
if (!dev)
return -EFAULT;
/* find func_idx pci_adapter and disable or enable nic */
lld_dev_hold();
list_for_each_entry(des_dev, &dev->chip_node->func_list, node) {
if (test_bit(HINIC_FUNC_IN_REMOVE, &dev->flag))
continue;
if (des_dev->init_state <
HINIC_INIT_STATE_DBGTOOL_INITED &&
!test_bit(HINIC_FUNC_PRB_ERR,
&des_dev->flag))
continue;
if (hinic_global_func_id(des_dev->hwdev) != vf_func_id)
continue;
if (des_dev->init_state <
HINIC_INIT_STATE_DBGTOOL_INITED) {
break;
}
sdk_info(&dev->pcidev->dev, "Receive event: %s vf%d nic\n",
en ? "enable" : "disable", vf_func_id);
err = 0;
if (en) {
if (des_dev->uld_dev[SERVICE_T_NIC]) {
sdk_err(&des_dev->pcidev->dev,
"%s driver has attached to pcie device, cannot set VF max_queue_num\n",
s_uld_name[SERVICE_T_NIC]);
} else {
err = hinic_set_vf_dev_cap(des_dev->hwdev);
if (err) {
sdk_err(&des_dev->pcidev->dev,
"%s driver Set VF max_queue_num failed, err=%d\n",
s_uld_name[SERVICE_T_NIC], err);
break;
}
}
err = attach_uld(des_dev, SERVICE_T_NIC,
&g_uld_info[SERVICE_T_NIC]);
if (err) {
sdk_err(&des_dev->pcidev->dev, "Failed to initialize NIC\n");
break;
}
uld_dev = (struct hinic_nic_dev *)
(des_dev->uld_dev[SERVICE_T_NIC]);
uld_dev->in_vm = true;
uld_dev->is_vm_slave =
is_multi_vm_slave(uld_dev->hwdev);
uld_dev->is_bm_slave =
is_multi_bm_slave(uld_dev->hwdev);
if (des_dev->init_state < HINIC_INIT_STATE_NIC_INITED)
des_dev->init_state =
HINIC_INIT_STATE_NIC_INITED;
} else {
detach_uld(des_dev, SERVICE_T_NIC);
}
break;
}
lld_dev_put();
return err;
}
EXPORT_SYMBOL(hinic_ovs_set_vf_nic_state);
static void slave_host_mgmt_work(struct work_struct *work)
{
struct hinic_pcidev *pci_adapter =
container_of(work, struct hinic_pcidev, slave_nic_work);
__set_nic_func_state(pci_adapter);
}
static void __multi_host_mgmt(struct hinic_pcidev *dev,
struct hinic_multi_host_mgmt_event *mhost_mgmt)
{
struct hinic_pcidev *des_dev;
struct hinic_mhost_nic_func_state *nic_state = {0};
switch (mhost_mgmt->sub_cmd) {
case HINIC_MHOST_NIC_STATE_CHANGE:
nic_state = mhost_mgmt->data;
nic_state->status = 0;
/* find func_idx pci_adapter and disable or enable nic */
lld_dev_hold();
list_for_each_entry(des_dev, &dev->chip_node->func_list, node) {
if (test_bit(HINIC_FUNC_IN_REMOVE, &des_dev->flag))
continue;
if (des_dev->init_state <
HINIC_INIT_STATE_DBGTOOL_INITED &&
!test_bit(HINIC_FUNC_PRB_ERR,
&des_dev->flag))
continue;
if (hinic_global_func_id_hw(des_dev->hwdev) !=
nic_state->func_idx)
continue;
if (des_dev->init_state <
HINIC_INIT_STATE_DBGTOOL_INITED) {
nic_state->status =
test_bit(HINIC_FUNC_PRB_ERR,
&des_dev->flag) ? 1 : 0;
break;
}
sdk_info(&dev->pcidev->dev, "Receive nic state changed event, state: %d\n",
nic_state->enable);
/* schedule_work */
schedule_work(&des_dev->slave_nic_work);
break;
}
lld_dev_put();
break;
default:
sdk_warn(&dev->pcidev->dev, "Received unknown multi-host mgmt event %d\n",
mhost_mgmt->sub_cmd);
break;
}
}
static void send_uld_dev_event(struct hinic_pcidev *dev,
struct hinic_event_info *event)
{
enum hinic_service_type type;
for (type = SERVICE_T_NIC; type < SERVICE_T_MAX; type++) {
if (test_and_set_bit(type, &dev->state)) {
sdk_warn(&dev->pcidev->dev, "Event: 0x%x can't handler, %s is in detach\n",
event->type, s_uld_name[type]);
continue;
}
if (g_uld_info[type].event)
g_uld_info[type].event(&dev->lld_dev,
dev->uld_dev[type], event);
clear_bit(type, &dev->state);
}
}
static void send_event_to_all_pf(struct hinic_pcidev *dev,
struct hinic_event_info *event)
{
struct hinic_pcidev *des_dev = NULL;
lld_dev_hold();
list_for_each_entry(des_dev, &dev->chip_node->func_list, node) {
if (test_bit(HINIC_FUNC_IN_REMOVE, &des_dev->flag) ||
des_dev->init_state < HINIC_INIT_STATE_HW_IF_INITED)
continue;
if (hinic_func_type(des_dev->hwdev) == TYPE_VF)
continue;
send_uld_dev_event(des_dev, event);
}
lld_dev_put();
}
static void send_event_to_dst_pf(struct hinic_pcidev *dev, u16 func_id,
struct hinic_event_info *event)
{
struct hinic_pcidev *des_dev = NULL;
lld_dev_hold();
list_for_each_entry(des_dev, &dev->chip_node->func_list, node) {
if (test_bit(HINIC_FUNC_IN_REMOVE, &des_dev->flag) ||
des_dev->init_state < HINIC_INIT_STATE_HW_IF_INITED)
continue;
if (hinic_func_type(des_dev->hwdev) == TYPE_VF)
continue;
if (hinic_global_func_id(des_dev->hwdev) == func_id) {
send_uld_dev_event(des_dev, event);
break;
}
}
lld_dev_put();
}
void hinic_event_process(void *adapter, struct hinic_event_info *event)
{
struct hinic_pcidev *dev = adapter;
u16 func_id;
switch (event->type) {
case HINIC_EVENT_FMW_ACT_NTC:
hinic_sync_time_to_fmw(dev);
break;
case HINIC_EVENT_MCTP_GET_HOST_INFO:
__mctp_get_host_info(dev, &event->mctp_info);
break;
case HINIC_EVENT_MULTI_HOST_MGMT:
__multi_host_mgmt(dev, &event->mhost_mgmt);
break;
case HINIC_EVENT_FAULT:
if (event->info.fault_level == FAULT_LEVEL_SERIOUS_FLR &&
event->info.event.chip.func_id < HINIC_MAX_PF_NUM) {
func_id = event->info.event.chip.func_id;
send_event_to_dst_pf(adapter, func_id, event);
} else {
send_uld_dev_event(adapter, event);
}
break;
case HINIC_EVENT_MGMT_WATCHDOG_EVENT:
send_event_to_all_pf(adapter, event);
break;
default:
send_uld_dev_event(adapter, event);
break;
}
}
static int mapping_bar(struct pci_dev *pdev, struct hinic_pcidev *pci_adapter)
{
u32 db_dwqe_size;
u64 dwqe_addr;
pci_adapter->cfg_reg_base =
pci_ioremap_bar(pdev, HINIC_PCI_CFG_REG_BAR);
if (!pci_adapter->cfg_reg_base) {
sdk_err(&pci_adapter->pcidev->dev,
"Failed to map configuration regs\n");
return -ENOMEM;
}
pci_adapter->intr_reg_base = pci_ioremap_bar(pdev,
HINIC_PCI_INTR_REG_BAR);
if (!pci_adapter->intr_reg_base) {
sdk_err(&pci_adapter->pcidev->dev,
"Failed to map interrupt regs\n");
goto map_intr_bar_err;
}
db_dwqe_size = hinic_get_db_size(pci_adapter->cfg_reg_base,
&pci_adapter->chip_mode);
pci_adapter->db_base_phy = pci_resource_start(pdev, HINIC_PCI_DB_BAR);
pci_adapter->db_base = ioremap(pci_adapter->db_base_phy,
db_dwqe_size);
if (!pci_adapter->db_base) {
sdk_err(&pci_adapter->pcidev->dev,
"Failed to map doorbell regs\n");
goto map_db_err;
}
if (pci_adapter->chip_mode != CHIP_MODE_NORMAL)
return 0;
dwqe_addr = pci_adapter->db_base_phy + db_dwqe_size;
#if defined(__aarch64__)
/* arm do not support call ioremap_wc() */
pci_adapter->dwqe_mapping = ioremap(dwqe_addr, db_dwqe_size);
#else
pci_adapter->dwqe_mapping = io_mapping_create_wc(dwqe_addr,
db_dwqe_size);
#endif
if (!pci_adapter->dwqe_mapping) {
sdk_err(&pci_adapter->pcidev->dev, "Failed to io_mapping_create_wc\n");
goto mapping_dwqe_err;
}
return 0;
mapping_dwqe_err:
iounmap(pci_adapter->db_base);
map_db_err:
iounmap(pci_adapter->intr_reg_base);
map_intr_bar_err:
iounmap(pci_adapter->cfg_reg_base);
return -ENOMEM;
}
static void unmapping_bar(struct hinic_pcidev *pci_adapter)
{
if (pci_adapter->chip_mode == CHIP_MODE_NORMAL) {
#if defined(__aarch64__)
iounmap(pci_adapter->dwqe_mapping);
#else
io_mapping_free(pci_adapter->dwqe_mapping);
#endif
}
iounmap(pci_adapter->db_base);
iounmap(pci_adapter->intr_reg_base);
iounmap(pci_adapter->cfg_reg_base);
}
static int alloc_chip_node(struct hinic_pcidev *pci_adapter)
{
struct card_node *chip_node;
unsigned char i;
unsigned char parent_bus_number = 0;
int err;
if (!pci_is_root_bus(pci_adapter->pcidev->bus))
parent_bus_number = pci_adapter->pcidev->bus->parent->number;
if (parent_bus_number != 0) {
list_for_each_entry(chip_node, &g_hinic_chip_list, node) {
if (chip_node->dp_bus_num == parent_bus_number) {
pci_adapter->chip_node = chip_node;
return 0;
}
}
} else if (pci_adapter->pcidev->device == HINIC_DEV_ID_1822_VF ||
pci_adapter->pcidev->device == HINIC_DEV_ID_1822_VF_HV) {
list_for_each_entry(chip_node, &g_hinic_chip_list, node) {
if (chip_node) {
pci_adapter->chip_node = chip_node;
return 0;
}
}
}
for (i = 0; i < MAX_CARD_ID; i++) {
if (!FIND_BIT(card_bit_map, i)) {
card_bit_map = (u64)SET_BIT(card_bit_map, i);
break;
}
}
if (i == MAX_CARD_ID) {
sdk_err(&pci_adapter->pcidev->dev,
"Failed to alloc card id\n");
return -EFAULT;
}
chip_node = kzalloc(sizeof(*chip_node), GFP_KERNEL);
if (!chip_node)
goto alloc_chip_err;
chip_node->dbgtool_attr_file.name = kzalloc(IFNAMSIZ, GFP_KERNEL);
if (!(chip_node->dbgtool_attr_file.name)) {
sdk_err(&pci_adapter->pcidev->dev,
"Failed to alloc dbgtool attr file name\n");
goto alloc_dbgtool_attr_file_err;
}
/* parent bus number */
chip_node->dp_bus_num = parent_bus_number;
err = snprintf(chip_node->chip_name, IFNAMSIZ, "%s%d",
HINIC_CHIP_NAME, i);
if (err <= 0 || err >= IFNAMSIZ) {
sdk_err(&pci_adapter->pcidev->dev,
"Failed to snprintf chip_name, function return(%d) and dest_len(%d)\n",
err, IFNAMSIZ);
goto alloc_dbgtool_attr_file_err;
}
err = snprintf((char *)chip_node->dbgtool_attr_file.name,
IFNAMSIZ, "%s%d", HINIC_CHIP_NAME, i);
if (err <= 0 || err >= IFNAMSIZ) {
sdk_err(&pci_adapter->pcidev->dev,
"Failed to snprintf dbgtool_attr_file_name, function return(%d) and dest_len(%d)\n",
err, IFNAMSIZ);
goto alloc_dbgtool_attr_file_err;
}
sdk_info(&pci_adapter->pcidev->dev,
"Add new chip %s to global list succeed\n",
chip_node->chip_name);
list_add_tail(&chip_node->node, &g_hinic_chip_list);
INIT_LIST_HEAD(&chip_node->func_list);
pci_adapter->chip_node = chip_node;
mutex_init(&chip_node->sfp_mutex);
return 0;
alloc_dbgtool_attr_file_err:
kfree(chip_node);
alloc_chip_err:
card_bit_map = CLEAR_BIT(card_bit_map, i);
return -ENOMEM;
}
static void free_chip_node(struct hinic_pcidev *pci_adapter)
{
struct card_node *chip_node = pci_adapter->chip_node;
u32 id;
int err;
if (!(card_bit_map & BIT(pci_adapter->card_id)))
return;
if (list_empty(&chip_node->func_list)) {
list_del(&chip_node->node);
sdk_info(&pci_adapter->pcidev->dev,
"Delete chip %s from global list succeed\n",
chip_node->chip_name);
err = sscanf(chip_node->chip_name, HINIC_CHIP_NAME "%u", &id);
if (err <= 0)
sdk_err(&pci_adapter->pcidev->dev, "Failed to get hinic id\n");
card_bit_map = CLEAR_BIT(card_bit_map, id);
kfree(chip_node->dbgtool_attr_file.name);
kfree(chip_node);
}
}
static bool hinic_get_vf_load_state(struct pci_dev *pdev)
{
unsigned char parent_bus_number;
struct card_node *chip_node;
u8 id;
if (!pdev->is_virtfn)
return false;
/* vf used in vm */
if (pci_is_root_bus(pdev->bus))
return disable_vf_load;
parent_bus_number = pdev->bus->parent->number;
lld_dev_hold();
list_for_each_entry(chip_node, &g_hinic_chip_list, node) {
if (chip_node->dp_bus_num == parent_bus_number) {
for (id = 0; id < HINIC_MAX_PF_NUM; id++) {
if (chip_node->pf_bus_num[id] ==
pdev->bus->number) {
lld_dev_put();
return chip_node->disable_vf_load[id];
}
}
}
}
lld_dev_put();
return disable_vf_load;
}
static void hinic_set_vf_load_state(struct hinic_pcidev *pci_adapter,
bool vf_load_state)
{
struct card_node *chip_node;
u16 func_id;
if (hinic_func_type(pci_adapter->hwdev) == TYPE_VF)
return;
/* The VF on the BM slave side must be probed */
if (is_multi_bm_slave(pci_adapter->hwdev))
vf_load_state = false;
func_id = hinic_global_func_id_hw(pci_adapter->hwdev);
chip_node = pci_adapter->chip_node;
chip_node->disable_vf_load[func_id] = vf_load_state;
chip_node->pf_bus_num[func_id] = pci_adapter->pcidev->bus->number;
sdk_info(&pci_adapter->pcidev->dev, "Current function support %s, %s vf load in host\n",
(hinic_support_ovs(pci_adapter->hwdev, NULL) ? "ovs" : "nic"),
(vf_load_state ? "disable" : "enable"));
}
int hinic_ovs_set_vf_load_state(struct pci_dev *pdev)
{
struct hinic_pcidev *pci_adapter;
if (!pdev) {
pr_err("pdev is null\n");
return -EINVAL;
}
pci_adapter = pci_get_drvdata(pdev);
if (!pci_adapter) {
pr_err("pci_adapter is null\n");
return -EFAULT;
}
hinic_set_vf_load_state(pci_adapter, disable_vf_load);
return 0;
}
EXPORT_SYMBOL(hinic_ovs_set_vf_load_state);
static int hinic_config_deft_mrss(struct pci_dev *pdev)
{
return 0;
}
static int hinic_config_pci_cto(struct pci_dev *pdev)
{
return 0;
}
static int hinic_pci_init(struct pci_dev *pdev)
{
struct hinic_pcidev *pci_adapter = NULL;
int err;
err = hinic_config_deft_mrss(pdev);
if (err) {
sdk_err(&pdev->dev, "Failed to configure Max Read Request Size\n");
return err;
}
err = hinic_config_pci_cto(pdev);
if (err) {
sdk_err(&pdev->dev, "Failed to configure Completion timeout\n");
return err;
}
pci_adapter = kzalloc(sizeof(*pci_adapter), GFP_KERNEL);
if (!pci_adapter)
return -ENOMEM;
pci_adapter->pcidev = pdev;
mutex_init(&pci_adapter->pdev_mutex);
pci_set_drvdata(pdev, pci_adapter);
#ifdef CONFIG_PCI_IOV
if (pdev->is_virtfn && hinic_get_vf_load_state(pdev)) {
sdk_info(&pdev->dev, "VFs are not binded to hinic\n");
return 0;
}
#endif
err = pci_enable_device(pdev);
if (err) {
sdk_err(&pdev->dev, "Failed to enable PCI device\n");
goto pci_enable_err;
}
err = pci_request_regions(pdev, HINIC_DRV_NAME);
if (err) {
sdk_err(&pdev->dev, "Failed to request regions\n");
goto pci_regions_err;
}
// pci_enable_pcie_error_reporting(pdev);
pci_set_master(pdev);
err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(64));
if (err) {
sdk_warn(&pdev->dev, "Couldn't set 64-bit DMA mask\n");
err = dma_set_mask(&pdev->dev, DMA_BIT_MASK(32));
if (err) {
sdk_err(&pdev->dev, "Failed to set DMA mask\n");
goto dma_mask_err;
}
}
err = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(64));
if (err) {
sdk_warn(&pdev->dev,
"Couldn't set 64-bit coherent DMA mask\n");
err = dma_set_coherent_mask(&pdev->dev, DMA_BIT_MASK(32));
if (err) {
sdk_err(&pdev->dev,
"Failed to set coherent DMA mask\n");
goto dma_consistnet_mask_err;
}
}
return 0;
dma_consistnet_mask_err:
dma_mask_err:
pci_clear_master(pdev);
// pci_disable_pcie_error_reporting(pdev);
pci_release_regions(pdev);
pci_regions_err:
pci_disable_device(pdev);
pci_enable_err:
pci_set_drvdata(pdev, NULL);
kfree(pci_adapter);
return err;
}
static void hinic_pci_deinit(struct pci_dev *pdev)
{
struct hinic_pcidev *pci_adapter = pci_get_drvdata(pdev);
pci_clear_master(pdev);
pci_release_regions(pdev);
// pci_disable_pcie_error_reporting(pdev);
pci_disable_device(pdev);
pci_set_drvdata(pdev, NULL);
kfree(pci_adapter);
}
static void hinic_notify_ppf_unreg(struct hinic_pcidev *pci_adapter)
{
struct card_node *chip_node = pci_adapter->chip_node;
struct hinic_pcidev *dev;
if (hinic_func_type(pci_adapter->hwdev) != TYPE_PPF)
return;
lld_lock_chip_node();
list_for_each_entry(dev, &chip_node->func_list, node) {
hinic_ppf_hwdev_unreg(dev->hwdev);
}
lld_unlock_chip_node();
}
static void hinic_notify_ppf_reg(struct hinic_pcidev *pci_adapter)
{
struct card_node *chip_node = pci_adapter->chip_node;
struct hinic_pcidev *dev;
if (hinic_func_type(pci_adapter->hwdev) != TYPE_PPF)
return;
lld_lock_chip_node();
list_for_each_entry(dev, &chip_node->func_list, node) {
hinic_ppf_hwdev_reg(dev->hwdev, pci_adapter->hwdev);
}
lld_unlock_chip_node();
}
#ifdef CONFIG_X86
/**
* cfg_order_reg - when cpu model is haswell or broadwell, should configure dma
* order register to zero
* @pci_adapter: pci adapter
*/
/*lint -save -e40 */
void cfg_order_reg(struct hinic_pcidev *pci_adapter)
{
u8 cpu_model[] = {0x3c, 0x3f, 0x45, 0x46, 0x3d, 0x47, 0x4f, 0x56};
struct cpuinfo_x86 *cpuinfo;
u32 i;
if (HINIC_FUNC_IS_VF(pci_adapter->hwdev))
return;
cpuinfo = &cpu_data(0);
for (i = 0; i < sizeof(cpu_model); i++) {
if (cpu_model[i] == cpuinfo->x86_model)
hinic_set_pcie_order_cfg(pci_adapter->hwdev);
}
}
/*lint -restore*/
#endif
static int hinic_func_init(struct pci_dev *pdev,
struct hinic_pcidev *pci_adapter)
{
struct hinic_init_para init_para;
bool vf_load_state;
int err;
init_para.adapter_hdl = pci_adapter;
init_para.pcidev_hdl = pdev;
init_para.dev_hdl = &pdev->dev;
init_para.cfg_reg_base = pci_adapter->cfg_reg_base;
init_para.intr_reg_base = pci_adapter->intr_reg_base;
init_para.db_base = pci_adapter->db_base;
init_para.db_base_phy = pci_adapter->db_base_phy;
init_para.dwqe_mapping = pci_adapter->dwqe_mapping;
init_para.hwdev = &pci_adapter->hwdev;
init_para.chip_node = pci_adapter->chip_node;
init_para.ppf_hwdev = hinic_get_ppf_hwdev_by_pdev(pdev);
err = hinic_init_hwdev(&init_para);
if (err < 0) {
pci_adapter->hwdev = NULL;
sdk_err(&pdev->dev, "Failed to initialize hardware device\n");
return -EFAULT;
} else if (err > 0) {
if (err == (1 << HINIC_HWDEV_ALL_INITED) &&
pci_adapter->init_state < HINIC_INIT_STATE_HW_IF_INITED) {
pci_adapter->init_state = HINIC_INIT_STATE_HW_IF_INITED;
sdk_info(&pdev->dev,
"Initialize hardware device later\n");
queue_delayed_work(pci_adapter->slave_nic_init_workq,
&pci_adapter->slave_nic_init_dwork,
HINIC_SLAVE_NIC_DELAY_TIME);
set_bit(HINIC_FUNC_PRB_DELAY, &pci_adapter->flag);
} else if (err != (1 << HINIC_HWDEV_ALL_INITED)) {
sdk_err(&pdev->dev,
"Initialize hardware device partitial failed\n");
hinic_detect_version_compatible(pci_adapter);
hinic_notify_ppf_reg(pci_adapter);
pci_adapter->init_state =
HINIC_INIT_STATE_HW_PART_INITED;
}
return -EFAULT;
}
hinic_notify_ppf_reg(pci_adapter);
pci_adapter->init_state = HINIC_INIT_STATE_HWDEV_INITED;
vf_load_state = hinic_support_ovs(pci_adapter->hwdev, NULL) ?
true : disable_vf_load;
hinic_set_vf_load_state(pci_adapter, vf_load_state);
hinic_qps_num_set(pci_adapter->hwdev, 0);
pci_adapter->lld_dev.pdev = pdev;
pci_adapter->lld_dev.hwdev = pci_adapter->hwdev;
pci_adapter->sriov_info.pdev = pdev;
pci_adapter->sriov_info.hwdev = pci_adapter->hwdev;
hinic_event_register(pci_adapter->hwdev, pci_adapter,
hinic_event_process);
if (!HINIC_FUNC_IS_VF(pci_adapter->hwdev))
hinic_sync_time_to_fmw(pci_adapter);
hinic_init_syncfw_timer(pci_adapter);
/* dbgtool init */
lld_lock_chip_node();
err = hinic_dbgtool_knl_init(pci_adapter->hwdev, pci_adapter->chip_node);
if (err) {
lld_unlock_chip_node();
sdk_err(&pdev->dev, "Failed to initialize dbgtool\n");
hinic_destroy_syncfw_timer(pci_adapter);
hinic_event_unregister(pci_adapter->hwdev);
return err;
}
lld_unlock_chip_node();
pci_adapter->init_state = HINIC_INIT_STATE_DBGTOOL_INITED;
err = hinic_detect_version_compatible(pci_adapter);
if (err)
return err;
if (!HINIC_FUNC_IS_VF(pci_adapter->hwdev) &&
FUNC_ENABLE_SRIOV_IN_DEFAULT(pci_adapter->hwdev)) {
hinic_pci_sriov_enable(pdev,
hinic_func_max_vf(pci_adapter->hwdev));
}
/* NIC is base driver, probe firstly */
err = __set_nic_func_state(pci_adapter);
if (err)
return err;
attach_ulds(pci_adapter);
#ifdef CONFIG_X86
cfg_order_reg(pci_adapter);
#endif
sdk_info(&pdev->dev, "Pcie device probed\n");
pci_adapter->init_state = HINIC_INIT_STATE_ALL_INITED;
return 0;
}
static void hinic_func_deinit(struct pci_dev *pdev)
{
struct hinic_pcidev *pci_adapter = pci_get_drvdata(pdev);
/* When function deinit, disable mgmt initiative report events firstly,
* then flush mgmt work-queue.
*/
hinic_disable_mgmt_msg_report(pci_adapter->hwdev);
if (pci_adapter->init_state >= HINIC_INIT_STATE_HW_PART_INITED)
hinic_flush_mgmt_workq(pci_adapter->hwdev);
hinic_set_func_deinit_flag(pci_adapter->hwdev);
if (pci_adapter->init_state >= HINIC_INIT_STATE_NIC_INITED) {
detach_ulds(pci_adapter);
detach_uld(pci_adapter, SERVICE_T_NIC);
}
if (pci_adapter->init_state >= HINIC_INIT_STATE_DBGTOOL_INITED) {
lld_lock_chip_node();
hinic_dbgtool_knl_deinit(pci_adapter->hwdev, pci_adapter->chip_node);
lld_unlock_chip_node();
hinic_destroy_syncfw_timer(pci_adapter);
hinic_event_unregister(pci_adapter->hwdev);
}
hinic_notify_ppf_unreg(pci_adapter);
if (pci_adapter->init_state >= HINIC_INIT_STATE_HW_IF_INITED) {
/* Remove the current node from node-list first,
* then it's safe to free hwdev
*/
lld_lock_chip_node();
list_del(&pci_adapter->node);
lld_unlock_chip_node();
hinic_free_hwdev(pci_adapter->hwdev);
}
}
static void wait_tool_unused(void)
{
u32 loop_cnt = 0;
while (loop_cnt < HINIC_WAIT_TOOL_CNT_TIMEOUT) {
if (!atomic_read(&tool_used_cnt))
return;
usleep_range(9900, 10000);
loop_cnt++;
}
}
static inline void wait_sriov_cfg_complete(struct hinic_pcidev *pci_adapter)
{
struct hinic_sriov_info *sriov_info;
u32 loop_cnt = 0;
sriov_info = &pci_adapter->sriov_info;
set_bit(HINIC_FUNC_REMOVE, &sriov_info->state);
usleep_range(9900, 10000);
while (loop_cnt < HINIC_WAIT_SRIOV_CFG_TIMEOUT) {
if (!test_bit(HINIC_SRIOV_ENABLE, &sriov_info->state) &&
!test_bit(HINIC_SRIOV_DISABLE, &sriov_info->state))
return;
usleep_range(9900, 10000);
loop_cnt++;
}
}
static void hinic_remove(struct pci_dev *pdev)
{
struct hinic_pcidev *pci_adapter = pci_get_drvdata(pdev);
if (!pci_adapter)
return;
sdk_info(&pdev->dev, "Pcie device remove begin\n");
#ifdef CONFIG_PCI_IOV
if (pdev->is_virtfn && hinic_get_vf_load_state(pdev)) {
pci_set_drvdata(pdev, NULL);
kfree(pci_adapter);
return;
}
#endif
cancel_delayed_work_sync(&pci_adapter->slave_nic_init_dwork);
flush_workqueue(pci_adapter->slave_nic_init_workq);
destroy_workqueue(pci_adapter->slave_nic_init_workq);
if (pci_adapter->init_state >= HINIC_INIT_STATE_HW_IF_INITED)
hinic_detect_hw_present(pci_adapter->hwdev);
switch (pci_adapter->init_state) {
case HINIC_INIT_STATE_ALL_INITED:
case HINIC_INIT_STATE_NIC_INITED:
/* Don't support hotplug when SR-IOV is enabled now.
* So disable SR-IOV capability as normal.
*/
if (!HINIC_FUNC_IS_VF(pci_adapter->hwdev)) {
wait_sriov_cfg_complete(pci_adapter);
hinic_pci_sriov_disable(pdev);
}
fallthrough;
case HINIC_INIT_STATE_DBGTOOL_INITED:
case HINIC_INIT_STATE_HWDEV_INITED:
case HINIC_INIT_STATE_HW_PART_INITED:
case HINIC_INIT_STATE_HW_IF_INITED:
case HINIC_INIT_STATE_PCI_INITED:
set_bit(HINIC_FUNC_IN_REMOVE, &pci_adapter->flag);
lld_lock_chip_node();
cancel_work_sync(&pci_adapter->slave_nic_work);
lld_unlock_chip_node();
wait_tool_unused();
if (pci_adapter->init_state >= HINIC_INIT_STATE_HW_IF_INITED)
hinic_func_deinit(pdev);
lld_lock_chip_node();
if (pci_adapter->init_state < HINIC_INIT_STATE_HW_IF_INITED)
list_del(&pci_adapter->node);
hinic_tool_k_uninit();
free_chip_node(pci_adapter);
lld_unlock_chip_node();
unmapping_bar(pci_adapter);
hinic_pci_deinit(pdev);
break;
default:
break;
}
sdk_info(&pdev->dev, "Pcie device removed\n");
}
static void slave_host_init_delay_work(struct work_struct *work)
{
struct delayed_work *delay = to_delayed_work(work);
struct hinic_pcidev *pci_adapter = container_of(delay,
struct hinic_pcidev, slave_nic_init_dwork);
struct pci_dev *pdev = pci_adapter->pcidev;
struct card_node *chip_node = pci_adapter->chip_node;
int found = 0;
struct hinic_pcidev *ppf_pcidev = NULL;
int err;
if (!hinic_get_master_host_mbox_enable(pci_adapter->hwdev)) {
queue_delayed_work(pci_adapter->slave_nic_init_workq,
&pci_adapter->slave_nic_init_dwork,
HINIC_SLAVE_NIC_DELAY_TIME);
return;
}
if (hinic_func_type(pci_adapter->hwdev) == TYPE_PPF) {
err = hinic_func_init(pdev, pci_adapter);
clear_bit(HINIC_FUNC_PRB_DELAY, &pci_adapter->flag);
if (err)
set_bit(HINIC_FUNC_PRB_ERR, &pci_adapter->flag);
return;
}
/* Make sure the PPF must be the first one */
lld_dev_hold();
list_for_each_entry(ppf_pcidev, &chip_node->func_list, node) {
if (test_bit(HINIC_FUNC_IN_REMOVE, &ppf_pcidev->flag) ||
ppf_pcidev->init_state < HINIC_INIT_STATE_HW_IF_INITED)
continue;
if (hinic_func_type(ppf_pcidev->hwdev) == TYPE_PPF) {
found = 1;
break;
}
}
lld_dev_put();
if (found && ppf_pcidev->init_state == HINIC_INIT_STATE_ALL_INITED) {
err = hinic_func_init(pdev, pci_adapter);
clear_bit(HINIC_FUNC_PRB_DELAY, &pci_adapter->flag);
if (err)
set_bit(HINIC_FUNC_PRB_ERR, &pci_adapter->flag);
} else {
queue_delayed_work(pci_adapter->slave_nic_init_workq,
&pci_adapter->slave_nic_init_dwork,
HINIC_SLAVE_NIC_DELAY_TIME);
}
}
static int hinic_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct hinic_pcidev *pci_adapter;
int err;
sdk_info(&pdev->dev, "Pcie device probe begin\n");
err = hinic_pci_init(pdev);
if (err)
return err;
#ifdef CONFIG_PCI_IOV
if (pdev->is_virtfn && hinic_get_vf_load_state(pdev))
return 0;
#endif
pci_adapter = pci_get_drvdata(pdev);
clear_bit(HINIC_FUNC_PRB_ERR, &pci_adapter->flag);
clear_bit(HINIC_FUNC_PRB_DELAY, &pci_adapter->flag);
err = mapping_bar(pdev, pci_adapter);
if (err) {
sdk_err(&pdev->dev, "Failed to map bar\n");
goto map_bar_failed;
}
pci_adapter->id = *id;
INIT_WORK(&pci_adapter->slave_nic_work, slave_host_mgmt_work);
pci_adapter->slave_nic_init_workq =
create_singlethread_workqueue(HINIC_SLAVE_NIC_DELAY);
if (!pci_adapter->slave_nic_init_workq) {
sdk_err(&pdev->dev,
"Failed to create work queue: %s\n",
HINIC_SLAVE_NIC_DELAY);
goto ceate_nic_delay_work_fail;
}
INIT_DELAYED_WORK(&pci_adapter->slave_nic_init_dwork,
slave_host_init_delay_work);
/* if chip information of pcie function exist,
* add the function into chip
*/
lld_lock_chip_node();
err = alloc_chip_node(pci_adapter);
if (err) {
sdk_err(&pdev->dev,
"Failed to add new chip node to global list\n");
goto alloc_chip_node_fail;
}
sscanf(pci_adapter->chip_node->chip_name, HINIC_CHIP_NAME "%d",
&pci_adapter->card_id);
err = hinic_tool_k_init();
if (err) {
sdk_warn(&pdev->dev, "Failed to init nictool");
goto init_nictool_err;
}
list_add_tail(&pci_adapter->node, &pci_adapter->chip_node->func_list);
lld_unlock_chip_node();
pci_adapter->init_state = HINIC_INIT_STATE_PCI_INITED;
err = hinic_func_init(pdev, pci_adapter);
if (err)
goto func_init_err;
return 0;
func_init_err:
if (!test_bit(HINIC_FUNC_PRB_DELAY, &pci_adapter->flag))
set_bit(HINIC_FUNC_PRB_ERR, &pci_adapter->flag);
return 0;
init_nictool_err:
free_chip_node(pci_adapter);
alloc_chip_node_fail:
lld_unlock_chip_node();
ceate_nic_delay_work_fail:
unmapping_bar(pci_adapter);
map_bar_failed:
hinic_pci_deinit(pdev);
sdk_err(&pdev->dev, "Pcie device probe failed\n");
return err;
}
/*lint -save -e133 -e10*/
static const struct pci_device_id hinic_pci_table[] = {
{PCI_VDEVICE(HUAWEI, HINIC_DEV_ID_1822_PF), HINIC_BOARD_25GE},
{PCI_VDEVICE(HUAWEI, HINIC_DEV_ID_1822_VF), 0},
{PCI_VDEVICE(HUAWEI, HINIC_DEV_ID_1822_VF_HV), 0},
{PCI_VDEVICE(HUAWEI, HINIC_DEV_ID_1822_SMTIO), HINIC_BOARD_PG_SM_25GE},
{PCI_VDEVICE(HUAWEI, HINIC_DEV_ID_1822_PANGEA_100GE),
HINIC_BOARD_PG_100GE},
{PCI_VDEVICE(HUAWEI, HINIC_DEV_ID_1822_PANGEA_TP_10GE),
HINIC_BOARD_PG_TP_10GE},
{PCI_VDEVICE(HUAWEI, HINIC_DEV_ID_1822_KR_40GE), HINIC_BOARD_40GE},
{PCI_VDEVICE(HUAWEI, HINIC_DEV_ID_1822_KR_100GE), HINIC_BOARD_100GE},
{PCI_VDEVICE(HUAWEI, HINIC_DEV_ID_1822_KR_25GE), HINIC_BOARD_25GE},
{PCI_VDEVICE(HUAWEI, HINIC_DEV_ID_1822_MULTI_HOST), HINIC_BOARD_25GE},
{PCI_VDEVICE(HUAWEI, HINIC_DEV_ID_1822_100GE), HINIC_BOARD_100GE},
{PCI_VDEVICE(HUAWEI, HINIC_DEV_ID_1822_DUAL_25GE), HINIC_BOARD_25GE},
{0, 0}
};
/*lint -restore*/
MODULE_DEVICE_TABLE(pci, hinic_pci_table);
/**
* hinic_io_error_detected - called when PCI error is detected
* @pdev: Pointer to PCI device
* @state: The current pci connection state
*
* This function is called after a PCI bus error affecting
* this device has been detected.
*
* Since we only need error detecting not error handling, so we
* always return PCI_ERS_RESULT_CAN_RECOVER to tell the AER
* driver that we don't need reset(error handling).
*/
static pci_ers_result_t hinic_io_error_detected(struct pci_dev *pdev,
pci_channel_state_t state)
{
struct hinic_pcidev *pci_adapter;
if (state == pci_channel_io_perm_failure)
return PCI_ERS_RESULT_DISCONNECT;
sdk_err(&pdev->dev,
"Uncorrectable error detected, log and cleanup error status: 0x%08x\n",
state);
pci_adapter = pci_get_drvdata(pdev);
if (pci_adapter)
hinic_record_pcie_error(pci_adapter->hwdev);
return PCI_ERS_RESULT_CAN_RECOVER;
}
static void hinic_shutdown(struct pci_dev *pdev)
{
struct hinic_pcidev *pci_adapter = pci_get_drvdata(pdev);
sdk_info(&pdev->dev, "Shutdown device\n");
if (pci_adapter)
hinic_shutdown_hwdev(pci_adapter->hwdev);
pci_disable_device(pdev);
if (pci_adapter)
hinic_set_api_stop(pci_adapter->hwdev);
}
/* Cause we only need error detecting not error handling, so only error_detected
* callback is enough.
*/
static struct pci_error_handlers hinic_err_handler = {
.error_detected = hinic_io_error_detected,
};
static struct pci_driver hinic_driver = {
.name = HINIC_DRV_NAME,
.id_table = hinic_pci_table,
.probe = hinic_probe,
.remove = hinic_remove,
.shutdown = hinic_shutdown,
.sriov_configure = hinic_pci_sriov_configure,
.err_handler = &hinic_err_handler
};
static int __init hinic_lld_init(void)
{
pr_info("%s - version %s\n", HINIC_DRV_DESC, HINIC_DRV_VERSION);
memset(g_uld_info, 0, sizeof(g_uld_info));
atomic_set(&tool_used_cnt, 0);
hinic_lld_lock_init();
/* register nic driver information first, and add net device in
* nic_probe called by hinic_probe.
*/
hinic_register_uld(SERVICE_T_NIC, &nic_uld_info);
return pci_register_driver(&hinic_driver);
}
static void __exit hinic_lld_exit(void)
{
pci_unregister_driver(&hinic_driver);
hinic_unregister_uld(SERVICE_T_NIC);
}
module_init(hinic_lld_init);
module_exit(hinic_lld_exit);
int hinic_register_micro_log(struct hinic_micro_log_info *micro_log_info)
{
struct card_node *chip_node;
struct hinic_pcidev *dev;
if (!micro_log_info || !micro_log_info->init ||
!micro_log_info->deinit) {
pr_err("Invalid information of micro log info to register\n");
return -EINVAL;
}
lld_dev_hold();
list_for_each_entry(chip_node, &g_hinic_chip_list, node) {
list_for_each_entry(dev, &chip_node->func_list, node) {
if (test_bit(HINIC_FUNC_IN_REMOVE, &dev->flag) ||
dev->init_state < HINIC_INIT_STATE_HW_IF_INITED)
continue;
if (hinic_func_type(dev->hwdev) == TYPE_PPF) {
if (micro_log_info->init(dev->hwdev)) {
sdk_err(&dev->pcidev->dev,
"micro log init failed\n");
continue;
}
}
}
}
lld_dev_put();
pr_info("Register micro log succeed\n");
return 0;
}
EXPORT_SYMBOL(hinic_register_micro_log);
void hinic_unregister_micro_log(struct hinic_micro_log_info *micro_log_info)
{
struct card_node *chip_node;
struct hinic_pcidev *dev;
if (!micro_log_info)
return;
lld_dev_hold();
list_for_each_entry(chip_node, &g_hinic_chip_list, node) {
list_for_each_entry(dev, &chip_node->func_list, node) {
if (test_bit(HINIC_FUNC_IN_REMOVE, &dev->flag) ||
dev->init_state < HINIC_INIT_STATE_HW_IF_INITED)
continue;
if (hinic_func_type(dev->hwdev) == TYPE_PPF)
micro_log_info->deinit(dev->hwdev);
}
}
lld_dev_put();
pr_info("Unregister micro log succeed\n");
}
EXPORT_SYMBOL(hinic_unregister_micro_log);