mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/chenhuacai/linux-loongson
synced 2025-09-01 06:39:05 +00:00

This is [1/3] part of hinic3 Ethernet driver initial submission. With this patch hinic3 is a valid kernel module but non-functional driver. The driver parts contained in this patch: Module initialization. PCI driver registration but with empty id_table. Auxiliary driver registration. Net device_ops registration but open/stop are empty stubs. tx/rx logic. All major data structures of the driver are fully introduced with the code that uses them but without their initialization code that requires management interface with the hw. Co-developed-by: Xin Guo <guoxin09@huawei.com> Signed-off-by: Xin Guo <guoxin09@huawei.com> Signed-off-by: Fan Gong <gongfan1@huawei.com> Co-developed-by: Gur Stavi <gur.stavi@huawei.com> Signed-off-by: Gur Stavi <gur.stavi@huawei.com> Link: https://patch.msgid.link/76a137ffdfe115c737c2c224f0c93b60ba53cc16.1747736586.git.gur.stavi@huawei.com Signed-off-by: Jakub Kicinski <kuba@kernel.org>
234 lines
6.4 KiB
C
234 lines
6.4 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
// Copyright (c) Huawei Technologies Co., Ltd. 2025. All rights reserved.
|
|
|
|
#include <linux/if_vlan.h>
|
|
|
|
#include "hinic3_hwdev.h"
|
|
#include "hinic3_hwif.h"
|
|
#include "hinic3_mbox.h"
|
|
#include "hinic3_nic_cfg.h"
|
|
#include "hinic3_nic_dev.h"
|
|
#include "hinic3_nic_io.h"
|
|
|
|
static int hinic3_feature_nego(struct hinic3_hwdev *hwdev, u8 opcode,
|
|
u64 *s_feature, u16 size)
|
|
{
|
|
struct l2nic_cmd_feature_nego feature_nego = {};
|
|
struct mgmt_msg_params msg_params = {};
|
|
int err;
|
|
|
|
feature_nego.func_id = hinic3_global_func_id(hwdev);
|
|
feature_nego.opcode = opcode;
|
|
if (opcode == MGMT_MSG_CMD_OP_SET)
|
|
memcpy(feature_nego.s_feature, s_feature, size * sizeof(u64));
|
|
|
|
mgmt_msg_params_init_default(&msg_params, &feature_nego,
|
|
sizeof(feature_nego));
|
|
|
|
err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_L2NIC,
|
|
L2NIC_CMD_FEATURE_NEGO, &msg_params);
|
|
if (err || feature_nego.msg_head.status) {
|
|
dev_err(hwdev->dev, "Failed to negotiate nic feature, err:%d, status: 0x%x\n",
|
|
err, feature_nego.msg_head.status);
|
|
return -EIO;
|
|
}
|
|
|
|
if (opcode == MGMT_MSG_CMD_OP_GET)
|
|
memcpy(s_feature, feature_nego.s_feature, size * sizeof(u64));
|
|
|
|
return 0;
|
|
}
|
|
|
|
int hinic3_set_nic_feature_to_hw(struct hinic3_nic_dev *nic_dev)
|
|
{
|
|
return hinic3_feature_nego(nic_dev->hwdev, MGMT_MSG_CMD_OP_SET,
|
|
&nic_dev->nic_io->feature_cap, 1);
|
|
}
|
|
|
|
bool hinic3_test_support(struct hinic3_nic_dev *nic_dev,
|
|
enum hinic3_nic_feature_cap feature_bits)
|
|
{
|
|
return (nic_dev->nic_io->feature_cap & feature_bits) == feature_bits;
|
|
}
|
|
|
|
void hinic3_update_nic_feature(struct hinic3_nic_dev *nic_dev, u64 feature_cap)
|
|
{
|
|
nic_dev->nic_io->feature_cap = feature_cap;
|
|
}
|
|
|
|
static int hinic3_set_function_table(struct hinic3_hwdev *hwdev, u32 cfg_bitmap,
|
|
const struct l2nic_func_tbl_cfg *cfg)
|
|
{
|
|
struct l2nic_cmd_set_func_tbl cmd_func_tbl = {};
|
|
struct mgmt_msg_params msg_params = {};
|
|
int err;
|
|
|
|
cmd_func_tbl.func_id = hinic3_global_func_id(hwdev);
|
|
cmd_func_tbl.cfg_bitmap = cfg_bitmap;
|
|
cmd_func_tbl.tbl_cfg = *cfg;
|
|
|
|
mgmt_msg_params_init_default(&msg_params, &cmd_func_tbl,
|
|
sizeof(cmd_func_tbl));
|
|
|
|
err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_L2NIC,
|
|
L2NIC_CMD_SET_FUNC_TBL, &msg_params);
|
|
if (err || cmd_func_tbl.msg_head.status) {
|
|
dev_err(hwdev->dev,
|
|
"Failed to set func table, bitmap: 0x%x, err: %d, status: 0x%x\n",
|
|
cfg_bitmap, err, cmd_func_tbl.msg_head.status);
|
|
return -EFAULT;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int hinic3_set_port_mtu(struct net_device *netdev, u16 new_mtu)
|
|
{
|
|
struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
|
|
struct l2nic_func_tbl_cfg func_tbl_cfg = {};
|
|
struct hinic3_hwdev *hwdev = nic_dev->hwdev;
|
|
|
|
func_tbl_cfg.mtu = new_mtu;
|
|
return hinic3_set_function_table(hwdev, BIT(L2NIC_FUNC_TBL_CFG_MTU),
|
|
&func_tbl_cfg);
|
|
}
|
|
|
|
static int hinic3_check_mac_info(struct hinic3_hwdev *hwdev, u8 status,
|
|
u16 vlan_id)
|
|
{
|
|
if ((status && status != MGMT_STATUS_EXIST) ||
|
|
((vlan_id & BIT(15)) && status == MGMT_STATUS_EXIST)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int hinic3_set_mac(struct hinic3_hwdev *hwdev, const u8 *mac_addr, u16 vlan_id,
|
|
u16 func_id)
|
|
{
|
|
struct l2nic_cmd_set_mac mac_info = {};
|
|
struct mgmt_msg_params msg_params = {};
|
|
int err;
|
|
|
|
if ((vlan_id & HINIC3_VLAN_ID_MASK) >= VLAN_N_VID) {
|
|
dev_err(hwdev->dev, "Invalid VLAN number: %d\n",
|
|
(vlan_id & HINIC3_VLAN_ID_MASK));
|
|
return -EINVAL;
|
|
}
|
|
|
|
mac_info.func_id = func_id;
|
|
mac_info.vlan_id = vlan_id;
|
|
ether_addr_copy(mac_info.mac, mac_addr);
|
|
|
|
mgmt_msg_params_init_default(&msg_params, &mac_info, sizeof(mac_info));
|
|
|
|
err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_L2NIC,
|
|
L2NIC_CMD_SET_MAC, &msg_params);
|
|
if (err || hinic3_check_mac_info(hwdev, mac_info.msg_head.status,
|
|
mac_info.vlan_id)) {
|
|
dev_err(hwdev->dev,
|
|
"Failed to update MAC, err: %d, status: 0x%x\n",
|
|
err, mac_info.msg_head.status);
|
|
return -EIO;
|
|
}
|
|
|
|
if (mac_info.msg_head.status == MGMT_STATUS_PF_SET_VF_ALREADY) {
|
|
dev_warn(hwdev->dev, "PF has already set VF mac, Ignore set operation\n");
|
|
return 0;
|
|
}
|
|
|
|
if (mac_info.msg_head.status == MGMT_STATUS_EXIST) {
|
|
dev_warn(hwdev->dev, "MAC is repeated. Ignore update operation\n");
|
|
return 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int hinic3_del_mac(struct hinic3_hwdev *hwdev, const u8 *mac_addr, u16 vlan_id,
|
|
u16 func_id)
|
|
{
|
|
struct l2nic_cmd_set_mac mac_info = {};
|
|
struct mgmt_msg_params msg_params = {};
|
|
int err;
|
|
|
|
if ((vlan_id & HINIC3_VLAN_ID_MASK) >= VLAN_N_VID) {
|
|
dev_err(hwdev->dev, "Invalid VLAN number: %d\n",
|
|
(vlan_id & HINIC3_VLAN_ID_MASK));
|
|
return -EINVAL;
|
|
}
|
|
|
|
mac_info.func_id = func_id;
|
|
mac_info.vlan_id = vlan_id;
|
|
ether_addr_copy(mac_info.mac, mac_addr);
|
|
|
|
mgmt_msg_params_init_default(&msg_params, &mac_info, sizeof(mac_info));
|
|
|
|
err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_L2NIC,
|
|
L2NIC_CMD_DEL_MAC, &msg_params);
|
|
if (err) {
|
|
dev_err(hwdev->dev,
|
|
"Failed to delete MAC, err: %d, status: 0x%x\n",
|
|
err, mac_info.msg_head.status);
|
|
return err;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int hinic3_update_mac(struct hinic3_hwdev *hwdev, const u8 *old_mac,
|
|
u8 *new_mac, u16 vlan_id, u16 func_id)
|
|
{
|
|
struct l2nic_cmd_update_mac mac_info = {};
|
|
struct mgmt_msg_params msg_params = {};
|
|
int err;
|
|
|
|
if ((vlan_id & HINIC3_VLAN_ID_MASK) >= VLAN_N_VID) {
|
|
dev_err(hwdev->dev, "Invalid VLAN number: %d\n",
|
|
(vlan_id & HINIC3_VLAN_ID_MASK));
|
|
return -EINVAL;
|
|
}
|
|
|
|
mac_info.func_id = func_id;
|
|
mac_info.vlan_id = vlan_id;
|
|
ether_addr_copy(mac_info.old_mac, old_mac);
|
|
ether_addr_copy(mac_info.new_mac, new_mac);
|
|
|
|
mgmt_msg_params_init_default(&msg_params, &mac_info, sizeof(mac_info));
|
|
|
|
err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_L2NIC,
|
|
L2NIC_CMD_UPDATE_MAC, &msg_params);
|
|
if (err || hinic3_check_mac_info(hwdev, mac_info.msg_head.status,
|
|
mac_info.vlan_id)) {
|
|
dev_err(hwdev->dev,
|
|
"Failed to update MAC, err: %d, status: 0x%x\n",
|
|
err, mac_info.msg_head.status);
|
|
return -EIO;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int hinic3_force_drop_tx_pkt(struct hinic3_hwdev *hwdev)
|
|
{
|
|
struct l2nic_cmd_force_pkt_drop pkt_drop = {};
|
|
struct mgmt_msg_params msg_params = {};
|
|
int err;
|
|
|
|
pkt_drop.port = hinic3_physical_port_id(hwdev);
|
|
|
|
mgmt_msg_params_init_default(&msg_params, &pkt_drop, sizeof(pkt_drop));
|
|
|
|
err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_L2NIC,
|
|
L2NIC_CMD_FORCE_PKT_DROP, &msg_params);
|
|
if ((pkt_drop.msg_head.status != MGMT_STATUS_CMD_UNSUPPORTED &&
|
|
pkt_drop.msg_head.status) || err) {
|
|
dev_err(hwdev->dev,
|
|
"Failed to set force tx packets drop, err: %d, status: 0x%x\n",
|
|
err, pkt_drop.msg_head.status);
|
|
return -EFAULT;
|
|
}
|
|
|
|
return pkt_drop.msg_head.status;
|
|
}
|