537 lines
13 KiB
C
537 lines
13 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
// Copyright (c) 2023 Hisilicon Limited.
|
|
|
|
#include "hns3_ext.h"
|
|
|
|
int nic_netdev_match_check(struct net_device *ndev)
|
|
{
|
|
#define HNS3_DRIVER_NAME_LEN 5
|
|
|
|
struct ethtool_drvinfo drv_info;
|
|
struct hnae3_handle *h;
|
|
|
|
if (!ndev || !ndev->ethtool_ops ||
|
|
!ndev->ethtool_ops->get_drvinfo)
|
|
return -EINVAL;
|
|
|
|
ndev->ethtool_ops->get_drvinfo(ndev, &drv_info);
|
|
|
|
if (strncmp(drv_info.driver, "hns3", HNS3_DRIVER_NAME_LEN))
|
|
return -EINVAL;
|
|
|
|
h = hns3_get_handle(ndev);
|
|
if (h->flags & HNAE3_SUPPORT_VF)
|
|
return -EINVAL;
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(nic_netdev_match_check);
|
|
|
|
static int nic_invoke_pri_ops(struct net_device *ndev, int opcode,
|
|
void *data, size_t length)
|
|
|
|
{
|
|
struct hnae3_handle *h;
|
|
int ret;
|
|
|
|
if (nic_netdev_match_check(ndev))
|
|
return -ENODEV;
|
|
|
|
if ((!data && length) || (data && !length)) {
|
|
netdev_err(ndev, "failed to check data and length\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
h = hns3_get_handle(ndev);
|
|
if (!h->ae_algo->ops->priv_ops)
|
|
return -EOPNOTSUPP;
|
|
|
|
ret = h->ae_algo->ops->priv_ops(h, opcode, data, length);
|
|
if (ret)
|
|
netdev_err(ndev,
|
|
"failed to invoke pri ops, opcode = %#x, ret = %d\n",
|
|
opcode, ret);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void nic_chip_recover_handler(struct net_device *ndev,
|
|
enum hnae3_event_type_custom event_t)
|
|
{
|
|
if (nic_netdev_match_check(ndev))
|
|
return;
|
|
|
|
dev_info(&ndev->dev, "reset type is %d!!\n", event_t);
|
|
|
|
if (event_t == HNAE3_PPU_POISON_CUSTOM)
|
|
event_t = HNAE3_FUNC_RESET_CUSTOM;
|
|
|
|
if (event_t != HNAE3_FUNC_RESET_CUSTOM &&
|
|
event_t != HNAE3_GLOBAL_RESET_CUSTOM &&
|
|
event_t != HNAE3_IMP_RESET_CUSTOM) {
|
|
dev_err(&ndev->dev, "reset type err!!\n");
|
|
return;
|
|
}
|
|
|
|
nic_invoke_pri_ops(ndev, HNAE3_EXT_OPC_RESET, &event_t, sizeof(event_t));
|
|
}
|
|
EXPORT_SYMBOL(nic_chip_recover_handler);
|
|
|
|
static int nic_check_pfc_storm_para(u32 dir, u32 enable, u32 period_ms,
|
|
u32 times, u32 recovery_period_ms)
|
|
{
|
|
if ((dir != HNS3_PFC_STORM_PARA_DIR_RX &&
|
|
dir != HNS3_PFC_STORM_PARA_DIR_TX) ||
|
|
(enable != HNS3_PFC_STORM_PARA_DISABLE &&
|
|
enable != HNS3_PFC_STORM_PARA_ENABLE))
|
|
return -EINVAL;
|
|
|
|
if (period_ms < HNS3_PFC_STORM_PARA_PERIOD_MIN ||
|
|
period_ms > HNS3_PFC_STORM_PARA_PERIOD_MAX ||
|
|
recovery_period_ms < HNS3_PFC_STORM_PARA_PERIOD_MIN ||
|
|
recovery_period_ms > HNS3_PFC_STORM_PARA_PERIOD_MAX ||
|
|
!times)
|
|
return -EINVAL;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int nic_set_pfc_storm_para(struct net_device *ndev, u32 dir, u32 enable,
|
|
u32 period_ms, u32 times, u32 recovery_period_ms)
|
|
{
|
|
struct hnae3_pfc_storm_para para;
|
|
|
|
if (nic_check_pfc_storm_para(dir, enable, period_ms, times,
|
|
recovery_period_ms)) {
|
|
pr_err("set pfc storm para failed because invalid input param.\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
para.dir = dir;
|
|
para.enable = enable;
|
|
para.period_ms = period_ms;
|
|
para.times = times;
|
|
para.recovery_period_ms = recovery_period_ms;
|
|
|
|
return nic_invoke_pri_ops(ndev, HNAE3_EXT_OPC_SET_PFC_STORM_PARA,
|
|
¶, sizeof(para));
|
|
}
|
|
EXPORT_SYMBOL(nic_set_pfc_storm_para);
|
|
|
|
int nic_get_pfc_storm_para(struct net_device *ndev, u32 dir, u32 *enable,
|
|
u32 *period_ms, u32 *times, u32 *recovery_period_ms)
|
|
{
|
|
struct hnae3_pfc_storm_para para;
|
|
int ret;
|
|
|
|
if (!enable || !period_ms || !times || !recovery_period_ms ||
|
|
(dir != HNS3_PFC_STORM_PARA_DIR_RX &&
|
|
dir != HNS3_PFC_STORM_PARA_DIR_TX)) {
|
|
pr_err("get pfc storm para failed because invalid input param.\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
para.dir = dir;
|
|
ret = nic_invoke_pri_ops(ndev, HNAE3_EXT_OPC_GET_PFC_STORM_PARA,
|
|
¶, sizeof(para));
|
|
if (ret)
|
|
return ret;
|
|
|
|
*enable = para.enable;
|
|
*period_ms = para.period_ms;
|
|
*times = para.times;
|
|
*recovery_period_ms = para.recovery_period_ms;
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(nic_get_pfc_storm_para);
|
|
|
|
int nic_set_notify_pkt_param(struct net_device *ndev,
|
|
struct hnae3_notify_pkt_param *param)
|
|
{
|
|
return nic_invoke_pri_ops(ndev, HNAE3_EXT_OPC_SET_NOTIFY_PARAM,
|
|
param, sizeof(*param));
|
|
}
|
|
EXPORT_SYMBOL(nic_set_notify_pkt_param);
|
|
|
|
int nic_set_notify_pkt_start(struct net_device *ndev)
|
|
{
|
|
return nic_invoke_pri_ops(ndev, HNAE3_EXT_OPC_SET_NOTIFY_START, NULL, 0);
|
|
}
|
|
EXPORT_SYMBOL(nic_set_notify_pkt_start);
|
|
|
|
int nic_set_torus_param(struct net_device *ndev, struct hnae3_torus_param *param)
|
|
{
|
|
if (!param || (param->enable != 0 && param->enable != 1))
|
|
return -EINVAL;
|
|
|
|
return nic_invoke_pri_ops(ndev, HNAE3_EXT_OPC_SET_TORUS_PARAM,
|
|
param, sizeof(*param));
|
|
}
|
|
EXPORT_SYMBOL(nic_set_torus_param);
|
|
|
|
int nic_get_torus_param(struct net_device *ndev, struct hnae3_torus_param *param)
|
|
{
|
|
return nic_invoke_pri_ops(ndev, HNAE3_EXT_OPC_GET_TORUS_PARAM,
|
|
param, sizeof(*param));
|
|
}
|
|
EXPORT_SYMBOL(nic_get_torus_param);
|
|
|
|
int nic_clean_stats64(struct net_device *ndev, struct rtnl_link_stats64 *stats)
|
|
{
|
|
struct hnae3_knic_private_info *kinfo;
|
|
struct hns3_enet_ring *ring;
|
|
struct hns3_nic_priv *priv;
|
|
struct hnae3_handle *h;
|
|
int i, ret;
|
|
|
|
if (nic_netdev_match_check(ndev))
|
|
return -ENODEV;
|
|
|
|
priv = netdev_priv(ndev);
|
|
h = hns3_get_handle(ndev);
|
|
kinfo = &h->kinfo;
|
|
|
|
rtnl_lock();
|
|
if (!test_bit(HNS3_NIC_STATE_INITED, &priv->state) ||
|
|
test_bit(HNS3_NIC_STATE_RESETTING, &priv->state)) {
|
|
ret = -EBUSY;
|
|
goto end_unlock;
|
|
}
|
|
|
|
ret = nic_invoke_pri_ops(ndev, HNAE3_EXT_OPC_CLEAN_STATS64,
|
|
NULL, 0);
|
|
if (ret)
|
|
goto end_unlock;
|
|
|
|
for (i = 0; i < kinfo->num_tqps; i++) {
|
|
ring = &priv->ring[i];
|
|
memset(&ring->stats, 0, sizeof(struct ring_stats));
|
|
ring = &priv->ring[i + kinfo->num_tqps];
|
|
memset(&ring->stats, 0, sizeof(struct ring_stats));
|
|
}
|
|
|
|
memset(&ndev->stats, 0, sizeof(struct net_device_stats));
|
|
netdev_info(ndev, "clean stats succ\n");
|
|
|
|
end_unlock:
|
|
rtnl_unlock();
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(nic_clean_stats64);
|
|
|
|
int nic_set_cpu_affinity(struct net_device *ndev, cpumask_t *affinity_mask)
|
|
{
|
|
struct hns3_enet_tqp_vector *tqp_vector;
|
|
struct hns3_nic_priv *priv;
|
|
int ret = 0;
|
|
u16 i;
|
|
|
|
if (nic_netdev_match_check(ndev))
|
|
return -ENODEV;
|
|
|
|
if (!affinity_mask) {
|
|
netdev_err(ndev,
|
|
"Invalid input param when set ethernet cpu affinity\n");
|
|
return -EINVAL;
|
|
}
|
|
|
|
priv = netdev_priv(ndev);
|
|
rtnl_lock();
|
|
if (!test_bit(HNS3_NIC_STATE_INITED, &priv->state) ||
|
|
test_bit(HNS3_NIC_STATE_RESETTING, &priv->state)) {
|
|
ret = -EBUSY;
|
|
goto err_unlock;
|
|
}
|
|
|
|
if (test_bit(HNS3_NIC_STATE_DOWN, &priv->state)) {
|
|
netdev_err(ndev,
|
|
"ethernet is down, not support cpu affinity set\n");
|
|
ret = -ENETDOWN;
|
|
goto err_unlock;
|
|
}
|
|
|
|
for (i = 0; i < priv->vector_num; i++) {
|
|
tqp_vector = &priv->tqp_vector[i];
|
|
if (tqp_vector->irq_init_flag != HNS3_VECTOR_INITED)
|
|
continue;
|
|
|
|
cpumask_copy(&tqp_vector->affinity_mask, affinity_mask);
|
|
|
|
ret = irq_set_affinity_hint(tqp_vector->vector_irq,
|
|
&tqp_vector->affinity_mask);
|
|
if (ret) {
|
|
netdev_err(ndev,
|
|
"failed to set affinity hint, ret = %d\n", ret);
|
|
goto err_unlock;
|
|
}
|
|
}
|
|
|
|
netdev_info(ndev, "set nic cpu affinity %*pb succeed\n",
|
|
cpumask_pr_args(affinity_mask));
|
|
|
|
err_unlock:
|
|
rtnl_unlock();
|
|
return ret;
|
|
}
|
|
EXPORT_SYMBOL(nic_set_cpu_affinity);
|
|
|
|
static int nic_get_ext_id_info(struct net_device *ndev,
|
|
struct hane3_port_ext_id_info *id_info)
|
|
{
|
|
return nic_invoke_pri_ops(ndev, HNAE3_EXT_OPC_GET_PORT_EXT_ID_INFO,
|
|
id_info, sizeof(*id_info));
|
|
}
|
|
|
|
int nic_get_chipid(struct net_device *ndev, u32 *chip_id)
|
|
{
|
|
struct hane3_port_ext_id_info info;
|
|
int ret;
|
|
|
|
if (!chip_id)
|
|
return -EINVAL;
|
|
|
|
ret = nic_get_ext_id_info(ndev, &info);
|
|
if (ret)
|
|
return ret;
|
|
|
|
*chip_id = info.chip_id;
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(nic_get_chipid);
|
|
|
|
int nic_get_mac_id(struct net_device *ndev, u32 *mac_id)
|
|
{
|
|
struct hane3_port_ext_id_info info;
|
|
int ret;
|
|
|
|
if (!mac_id)
|
|
return -EINVAL;
|
|
|
|
ret = nic_get_ext_id_info(ndev, &info);
|
|
if (ret)
|
|
return ret;
|
|
|
|
*mac_id = info.mac_id;
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(nic_get_mac_id);
|
|
|
|
int nic_get_io_die_id(struct net_device *ndev, u32 *io_die_id)
|
|
{
|
|
struct hane3_port_ext_id_info info;
|
|
int ret;
|
|
|
|
if (!io_die_id)
|
|
return -EINVAL;
|
|
|
|
ret = nic_get_ext_id_info(ndev, &info);
|
|
if (ret)
|
|
return ret;
|
|
|
|
*io_die_id = info.io_die_id;
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(nic_get_io_die_id);
|
|
|
|
static int nic_get_ext_num_info(struct net_device *ndev,
|
|
struct hane3_port_ext_num_info *num_info)
|
|
{
|
|
return nic_invoke_pri_ops(ndev, HNAE3_EXT_OPC_GET_PORT_EXT_NUM_INFO,
|
|
num_info, sizeof(*num_info));
|
|
}
|
|
|
|
int nic_get_chip_num(struct net_device *ndev, u32 *chip_num)
|
|
{
|
|
struct hane3_port_ext_num_info info;
|
|
int ret;
|
|
|
|
if (!chip_num)
|
|
return -EINVAL;
|
|
|
|
ret = nic_get_ext_num_info(ndev, &info);
|
|
if (ret)
|
|
return ret;
|
|
|
|
*chip_num = info.chip_num;
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(nic_get_chip_num);
|
|
|
|
int nic_get_io_die_num(struct net_device *ndev, u32 *io_die_num)
|
|
{
|
|
struct hane3_port_ext_num_info info;
|
|
int ret;
|
|
|
|
if (!io_die_num)
|
|
return -EINVAL;
|
|
|
|
ret = nic_get_ext_num_info(ndev, &info);
|
|
if (ret)
|
|
return ret;
|
|
|
|
*io_die_num = info.io_die_num;
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(nic_get_io_die_num);
|
|
|
|
int nic_get_port_num_of_die(struct net_device *ndev, u32 *port_num)
|
|
{
|
|
return nic_invoke_pri_ops(ndev, HNAE3_EXT_OPC_GET_PORT_NUM,
|
|
port_num, sizeof(*port_num));
|
|
}
|
|
EXPORT_SYMBOL(nic_get_port_num_of_die);
|
|
|
|
int nic_get_port_num_per_chip(struct net_device *ndev, u32 *port_num)
|
|
{
|
|
return nic_get_port_num_of_die(ndev, port_num);
|
|
}
|
|
EXPORT_SYMBOL(nic_get_port_num_per_chip);
|
|
|
|
int nic_set_tx_timeout(struct net_device *ndev, int tx_timeout)
|
|
{
|
|
if (nic_netdev_match_check(ndev))
|
|
return -ENODEV;
|
|
|
|
if (tx_timeout <= 0 || tx_timeout > HNS3_MAX_TX_TIMEOUT)
|
|
return -EINVAL;
|
|
|
|
ndev->watchdog_timeo = tx_timeout * HZ;
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(nic_set_tx_timeout);
|
|
|
|
int nic_get_sfp_present(struct net_device *ndev, int *present)
|
|
{
|
|
return nic_invoke_pri_ops(ndev, HNAE3_EXT_OPC_GET_PRESENT,
|
|
present, sizeof(*present));
|
|
}
|
|
EXPORT_SYMBOL(nic_get_sfp_present);
|
|
|
|
int nic_set_sfp_state(struct net_device *ndev, bool en)
|
|
{
|
|
u32 state = en ? 1 : 0;
|
|
|
|
return nic_invoke_pri_ops(ndev, HNAE3_EXT_OPC_SET_SFP_STATE,
|
|
&state, sizeof(state));
|
|
}
|
|
EXPORT_SYMBOL(nic_set_sfp_state);
|
|
|
|
int nic_disable_net_lane(struct net_device *ndev)
|
|
{
|
|
return nic_invoke_pri_ops(ndev, HNAE3_EXT_OPC_DISABLE_LANE, NULL, 0);
|
|
}
|
|
EXPORT_SYMBOL(nic_disable_net_lane);
|
|
|
|
int nic_get_net_lane_status(struct net_device *ndev, u32 *status)
|
|
{
|
|
return nic_invoke_pri_ops(ndev, HNAE3_EXT_OPC_GET_LANE_STATUS,
|
|
status, sizeof(*status));
|
|
}
|
|
EXPORT_SYMBOL(nic_get_net_lane_status);
|
|
|
|
int nic_disable_clock(struct net_device *ndev)
|
|
{
|
|
return nic_invoke_pri_ops(ndev, HNAE3_EXT_OPC_DISABLE_CLOCK,
|
|
NULL, 0);
|
|
}
|
|
EXPORT_SYMBOL(nic_disable_clock);
|
|
|
|
int nic_set_pfc_time_cfg(struct net_device *ndev, u16 time)
|
|
{
|
|
return nic_invoke_pri_ops(ndev, HNAE3_EXT_OPC_SET_PFC_TIME,
|
|
&time, sizeof(time));
|
|
}
|
|
EXPORT_SYMBOL(nic_set_pfc_time_cfg);
|
|
|
|
int nic_get_port_fault_status(struct net_device *ndev, u32 fault_type, u32 *status)
|
|
{
|
|
int opcode = HNAE3_EXT_OPC_GET_PORT_FAULT_STATUS;
|
|
struct hnae3_port_fault fault_para;
|
|
int ret;
|
|
|
|
if (!status)
|
|
return -EINVAL;
|
|
|
|
if (fault_type == HNAE3_FAULT_TYPE_HILINK_REF_LOS)
|
|
opcode = HNAE3_EXT_OPC_GET_HILINK_REF_LOS;
|
|
|
|
fault_para.fault_type = fault_type;
|
|
ret = nic_invoke_pri_ops(ndev, opcode, &fault_para, sizeof(fault_para));
|
|
if (ret)
|
|
return ret;
|
|
|
|
*status = fault_para.fault_status;
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(nic_get_port_fault_status);
|
|
|
|
int nic_get_port_wire_type(struct net_device *ndev, u32 *wire_type)
|
|
{
|
|
return nic_invoke_pri_ops(ndev, HNAE3_EXT_OPC_GET_PORT_TYPE,
|
|
wire_type, sizeof(*wire_type));
|
|
}
|
|
EXPORT_SYMBOL(nic_get_port_wire_type);
|
|
|
|
int nic_set_mac_state(struct net_device *ndev, int enable)
|
|
{
|
|
return nic_invoke_pri_ops(ndev, HNAE3_EXT_OPC_SET_MAC_STATE,
|
|
&enable, sizeof(enable));
|
|
}
|
|
EXPORT_SYMBOL(nic_set_mac_state);
|
|
|
|
int nic_set_led(struct net_device *ndev, u32 type, u32 status)
|
|
{
|
|
struct hnae3_led_state_para para;
|
|
|
|
para.status = status;
|
|
para.type = type;
|
|
|
|
return nic_invoke_pri_ops(ndev, HNAE3_EXT_OPC_SET_LED,
|
|
¶, sizeof(para));
|
|
}
|
|
EXPORT_SYMBOL(nic_set_led);
|
|
|
|
int nic_get_led_signal(struct net_device *ndev, struct hnae3_lamp_signal *signal)
|
|
{
|
|
return nic_invoke_pri_ops(ndev, HNAE3_EXT_OPC_GET_LED_SIGNAL,
|
|
signal, sizeof(*signal));
|
|
}
|
|
EXPORT_SYMBOL(nic_get_led_signal);
|
|
|
|
int nic_get_phy_reg(struct net_device *ndev, u32 page_select_addr,
|
|
u16 page, u32 reg_addr, u16 *data)
|
|
{
|
|
struct hnae3_phy_para para;
|
|
int ret;
|
|
|
|
if (!data)
|
|
return -EINVAL;
|
|
|
|
para.page_select_addr = page_select_addr;
|
|
para.page = page;
|
|
para.reg_addr = reg_addr;
|
|
ret = nic_invoke_pri_ops(ndev, HNAE3_EXT_OPC_GET_PHY_REG,
|
|
¶, sizeof(para));
|
|
if (ret)
|
|
return ret;
|
|
|
|
*data = para.data;
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL(nic_get_phy_reg);
|
|
|
|
int nic_set_phy_reg(struct net_device *ndev, u32 page_select_addr,
|
|
u16 page, u32 reg_addr, u16 data)
|
|
{
|
|
struct hnae3_phy_para para;
|
|
|
|
para.page_select_addr = page_select_addr;
|
|
para.page = page;
|
|
para.reg_addr = reg_addr;
|
|
para.data = data;
|
|
return nic_invoke_pri_ops(ndev, HNAE3_EXT_OPC_SET_PHY_REG,
|
|
¶, sizeof(para));
|
|
}
|
|
EXPORT_SYMBOL(nic_set_phy_reg);
|