2924 lines
70 KiB
C
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);
|