]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
hinic3: Add ethtool basic ops
authorFan Gong <gongfan1@huawei.com>
Tue, 10 Mar 2026 01:04:57 +0000 (09:04 +0800)
committerPaolo Abeni <pabeni@redhat.com>
Thu, 12 Mar 2026 11:13:48 +0000 (12:13 +0100)
Implement following ethtool callback function:
.get_link_ksettings
.get_drvinfo
.get_msglevel
.set_msglevel
.get_link

  These callbacks allow users to utilize ethtool for detailed
network configuration and monitoring.

Co-developed-by: Zhu Yikai <zhuyikai1@h-partners.com>
Signed-off-by: Zhu Yikai <zhuyikai1@h-partners.com>
Signed-off-by: Fan Gong <gongfan1@huawei.com>
Link: https://patch.msgid.link/b56d490c2a06cae9541a0297d76b11d869f37161.1773062356.git.zhuyikai1@h-partners.com
Signed-off-by: Paolo Abeni <pabeni@redhat.com>
drivers/net/ethernet/huawei/hinic3/Makefile
drivers/net/ethernet/huawei/hinic3/hinic3_ethtool.c [new file with mode: 0644]
drivers/net/ethernet/huawei/hinic3/hinic3_hw_comm.c
drivers/net/ethernet/huawei/hinic3/hinic3_hw_comm.h
drivers/net/ethernet/huawei/hinic3/hinic3_hw_intf.h
drivers/net/ethernet/huawei/hinic3/hinic3_main.c
drivers/net/ethernet/huawei/hinic3/hinic3_mgmt_interface.h
drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.c
drivers/net/ethernet/huawei/hinic3/hinic3_nic_cfg.h
drivers/net/ethernet/huawei/hinic3/hinic3_nic_dev.h
drivers/net/ethernet/huawei/hinic3/hinic3_rss.c

index 26c05ecf31c9059a107ba31dcb87e52c3fbd695f..4ebad3a4f94398983dddf5e4820e85b3be026fee 100644 (file)
@@ -6,6 +6,7 @@ obj-$(CONFIG_HINIC3) += hinic3.o
 hinic3-objs := hinic3_cmdq.o \
               hinic3_common.o \
               hinic3_eqs.o \
+              hinic3_ethtool.o \
               hinic3_filter.o \
               hinic3_hw_cfg.o \
               hinic3_hw_comm.o \
diff --git a/drivers/net/ethernet/huawei/hinic3/hinic3_ethtool.c b/drivers/net/ethernet/huawei/hinic3/hinic3_ethtool.c
new file mode 100644 (file)
index 0000000..90fc162
--- /dev/null
@@ -0,0 +1,425 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright (c) Huawei Technologies Co., Ltd. 2026. All rights reserved.
+
+#include <linux/kernel.h>
+#include <linux/pci.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/errno.h>
+#include <linux/etherdevice.h>
+#include <linux/netdevice.h>
+#include <linux/ethtool.h>
+
+#include "hinic3_lld.h"
+#include "hinic3_hw_comm.h"
+#include "hinic3_nic_dev.h"
+#include "hinic3_nic_cfg.h"
+
+#define HINIC3_MGMT_VERSION_MAX_LEN     32
+
+static void hinic3_get_drvinfo(struct net_device *netdev,
+                              struct ethtool_drvinfo *info)
+{
+       struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+       u8 mgmt_ver[HINIC3_MGMT_VERSION_MAX_LEN];
+       struct pci_dev *pdev = nic_dev->pdev;
+       int err;
+
+       strscpy(info->driver, HINIC3_NIC_DRV_NAME, sizeof(info->driver));
+       strscpy(info->bus_info, pci_name(pdev), sizeof(info->bus_info));
+
+       err = hinic3_get_mgmt_version(nic_dev->hwdev, mgmt_ver,
+                                     HINIC3_MGMT_VERSION_MAX_LEN);
+       if (err) {
+               netdev_err(netdev, "Failed to get fw version\n");
+               return;
+       }
+
+       snprintf(info->fw_version, sizeof(info->fw_version), "%s", mgmt_ver);
+}
+
+static u32 hinic3_get_msglevel(struct net_device *netdev)
+{
+       struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+
+       return nic_dev->msg_enable;
+}
+
+static void hinic3_set_msglevel(struct net_device *netdev, u32 data)
+{
+       struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+
+       nic_dev->msg_enable = data;
+
+       netdev_dbg(netdev, "Set message level: 0x%x\n", data);
+}
+
+static const u32 hinic3_link_mode_ge[] = {
+       ETHTOOL_LINK_MODE_1000baseT_Full_BIT,
+       ETHTOOL_LINK_MODE_1000baseKX_Full_BIT,
+       ETHTOOL_LINK_MODE_1000baseX_Full_BIT,
+};
+
+static const u32 hinic3_link_mode_10ge_base_r[] = {
+       ETHTOOL_LINK_MODE_10000baseKR_Full_BIT,
+       ETHTOOL_LINK_MODE_10000baseR_FEC_BIT,
+       ETHTOOL_LINK_MODE_10000baseCR_Full_BIT,
+       ETHTOOL_LINK_MODE_10000baseSR_Full_BIT,
+       ETHTOOL_LINK_MODE_10000baseLR_Full_BIT,
+       ETHTOOL_LINK_MODE_10000baseLRM_Full_BIT,
+};
+
+static const u32 hinic3_link_mode_25ge_base_r[] = {
+       ETHTOOL_LINK_MODE_25000baseCR_Full_BIT,
+       ETHTOOL_LINK_MODE_25000baseKR_Full_BIT,
+       ETHTOOL_LINK_MODE_25000baseSR_Full_BIT,
+};
+
+static const u32 hinic3_link_mode_40ge_base_r4[] = {
+       ETHTOOL_LINK_MODE_40000baseKR4_Full_BIT,
+       ETHTOOL_LINK_MODE_40000baseCR4_Full_BIT,
+       ETHTOOL_LINK_MODE_40000baseSR4_Full_BIT,
+       ETHTOOL_LINK_MODE_40000baseLR4_Full_BIT,
+};
+
+static const u32 hinic3_link_mode_50ge_base_r[] = {
+       ETHTOOL_LINK_MODE_50000baseKR_Full_BIT,
+       ETHTOOL_LINK_MODE_50000baseSR_Full_BIT,
+       ETHTOOL_LINK_MODE_50000baseCR_Full_BIT,
+};
+
+static const u32 hinic3_link_mode_50ge_base_r2[] = {
+       ETHTOOL_LINK_MODE_50000baseCR2_Full_BIT,
+       ETHTOOL_LINK_MODE_50000baseKR2_Full_BIT,
+       ETHTOOL_LINK_MODE_50000baseSR2_Full_BIT,
+};
+
+static const u32 hinic3_link_mode_100ge_base_r[] = {
+       ETHTOOL_LINK_MODE_100000baseKR_Full_BIT,
+       ETHTOOL_LINK_MODE_100000baseSR_Full_BIT,
+       ETHTOOL_LINK_MODE_100000baseCR_Full_BIT,
+};
+
+static const u32 hinic3_link_mode_100ge_base_r2[] = {
+       ETHTOOL_LINK_MODE_100000baseKR2_Full_BIT,
+       ETHTOOL_LINK_MODE_100000baseSR2_Full_BIT,
+       ETHTOOL_LINK_MODE_100000baseCR2_Full_BIT,
+};
+
+static const u32 hinic3_link_mode_100ge_base_r4[] = {
+       ETHTOOL_LINK_MODE_100000baseKR4_Full_BIT,
+       ETHTOOL_LINK_MODE_100000baseSR4_Full_BIT,
+       ETHTOOL_LINK_MODE_100000baseCR4_Full_BIT,
+       ETHTOOL_LINK_MODE_100000baseLR4_ER4_Full_BIT,
+};
+
+static const u32 hinic3_link_mode_200ge_base_r2[] = {
+       ETHTOOL_LINK_MODE_200000baseKR2_Full_BIT,
+       ETHTOOL_LINK_MODE_200000baseSR2_Full_BIT,
+       ETHTOOL_LINK_MODE_200000baseCR2_Full_BIT,
+};
+
+static const u32 hinic3_link_mode_200ge_base_r4[] = {
+       ETHTOOL_LINK_MODE_200000baseKR4_Full_BIT,
+       ETHTOOL_LINK_MODE_200000baseSR4_Full_BIT,
+       ETHTOOL_LINK_MODE_200000baseCR4_Full_BIT,
+};
+
+struct hw2ethtool_link_mode {
+       const u32 *link_mode_bit_arr;
+       u32       arr_size;
+       u32       speed;
+};
+
+static const struct hw2ethtool_link_mode
+       hw2ethtool_link_mode_table[LINK_MODE_MAX_NUMBERS] = {
+       [LINK_MODE_GE] = {
+               .link_mode_bit_arr = hinic3_link_mode_ge,
+               .arr_size          = ARRAY_SIZE(hinic3_link_mode_ge),
+               .speed             = SPEED_1000,
+       },
+       [LINK_MODE_10GE_BASE_R] = {
+               .link_mode_bit_arr = hinic3_link_mode_10ge_base_r,
+               .arr_size          = ARRAY_SIZE(hinic3_link_mode_10ge_base_r),
+               .speed             = SPEED_10000,
+       },
+       [LINK_MODE_25GE_BASE_R] = {
+               .link_mode_bit_arr = hinic3_link_mode_25ge_base_r,
+               .arr_size          = ARRAY_SIZE(hinic3_link_mode_25ge_base_r),
+               .speed             = SPEED_25000,
+       },
+       [LINK_MODE_40GE_BASE_R4] = {
+               .link_mode_bit_arr = hinic3_link_mode_40ge_base_r4,
+               .arr_size          = ARRAY_SIZE(hinic3_link_mode_40ge_base_r4),
+               .speed             = SPEED_40000,
+       },
+       [LINK_MODE_50GE_BASE_R] = {
+               .link_mode_bit_arr = hinic3_link_mode_50ge_base_r,
+               .arr_size          = ARRAY_SIZE(hinic3_link_mode_50ge_base_r),
+               .speed             = SPEED_50000,
+       },
+       [LINK_MODE_50GE_BASE_R2] = {
+               .link_mode_bit_arr = hinic3_link_mode_50ge_base_r2,
+               .arr_size          = ARRAY_SIZE(hinic3_link_mode_50ge_base_r2),
+               .speed             = SPEED_50000,
+       },
+       [LINK_MODE_100GE_BASE_R] = {
+               .link_mode_bit_arr = hinic3_link_mode_100ge_base_r,
+               .arr_size          = ARRAY_SIZE(hinic3_link_mode_100ge_base_r),
+               .speed             = SPEED_100000,
+       },
+       [LINK_MODE_100GE_BASE_R2] = {
+               .link_mode_bit_arr = hinic3_link_mode_100ge_base_r2,
+               .arr_size          = ARRAY_SIZE(hinic3_link_mode_100ge_base_r2),
+               .speed             = SPEED_100000,
+       },
+       [LINK_MODE_100GE_BASE_R4] = {
+               .link_mode_bit_arr = hinic3_link_mode_100ge_base_r4,
+               .arr_size          = ARRAY_SIZE(hinic3_link_mode_100ge_base_r4),
+               .speed             = SPEED_100000,
+       },
+       [LINK_MODE_200GE_BASE_R2] = {
+               .link_mode_bit_arr = hinic3_link_mode_200ge_base_r2,
+               .arr_size          = ARRAY_SIZE(hinic3_link_mode_200ge_base_r2),
+               .speed             = SPEED_200000,
+       },
+       [LINK_MODE_200GE_BASE_R4] = {
+               .link_mode_bit_arr = hinic3_link_mode_200ge_base_r4,
+               .arr_size          = ARRAY_SIZE(hinic3_link_mode_200ge_base_r4),
+               .speed             = SPEED_200000,
+       },
+};
+
+#define GET_SUPPORTED_MODE     0
+#define GET_ADVERTISED_MODE    1
+
+struct hinic3_link_settings {
+       __ETHTOOL_DECLARE_LINK_MODE_MASK(supported);
+       __ETHTOOL_DECLARE_LINK_MODE_MASK(advertising);
+
+       u32 speed;
+       u8  duplex;
+       u8  port;
+       u8  autoneg;
+};
+
+#define HINIC3_ADD_SUPPORTED_LINK_MODE(ecmd, mode) \
+       set_bit(ETHTOOL_LINK_##mode##_BIT, (ecmd)->supported)
+#define HINIC3_ADD_ADVERTISED_LINK_MODE(ecmd, mode) \
+       set_bit(ETHTOOL_LINK_##mode##_BIT, (ecmd)->advertising)
+
+static void hinic3_add_speed_link_mode(unsigned long *bitmap, u32 mode)
+{
+       u32 i;
+
+       for (i = 0; i < hw2ethtool_link_mode_table[mode].arr_size; i++) {
+               if (hw2ethtool_link_mode_table[mode].link_mode_bit_arr[i] >=
+                   __ETHTOOL_LINK_MODE_MASK_NBITS)
+                       continue;
+
+               set_bit(hw2ethtool_link_mode_table[mode].link_mode_bit_arr[i],
+                       bitmap);
+       }
+}
+
+/* Related to enum mag_cmd_port_speed */
+static const u32 hw_to_ethtool_speed[] = {
+       (u32)SPEED_UNKNOWN, SPEED_10,    SPEED_100,   SPEED_1000,   SPEED_10000,
+       SPEED_25000,        SPEED_40000, SPEED_50000, SPEED_100000, SPEED_200000
+};
+
+static void
+hinic3_add_ethtool_link_mode(struct hinic3_link_settings *link_settings,
+                            u32 hw_link_mode, u32 name)
+{
+       unsigned long *advertising_mask = link_settings->advertising;
+       unsigned long *supported_mask = link_settings->supported;
+       u32 link_mode;
+
+       for (link_mode = 0; link_mode < LINK_MODE_MAX_NUMBERS; link_mode++) {
+               if (hw_link_mode & BIT(link_mode)) {
+                       if (name == GET_SUPPORTED_MODE)
+                               hinic3_add_speed_link_mode(supported_mask,
+                                                          link_mode);
+                       else
+                               hinic3_add_speed_link_mode(advertising_mask,
+                                                          link_mode);
+               }
+       }
+}
+
+static void
+hinic3_link_speed_set(struct net_device *netdev,
+                     struct hinic3_link_settings *link_settings,
+                     struct hinic3_nic_port_info *port_info)
+{
+       struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+       bool link_status_up;
+       int err;
+
+       if (port_info->supported_mode != LINK_MODE_UNKNOWN)
+               hinic3_add_ethtool_link_mode(link_settings,
+                                            port_info->supported_mode,
+                                            GET_SUPPORTED_MODE);
+       if (port_info->advertised_mode != LINK_MODE_UNKNOWN)
+               hinic3_add_ethtool_link_mode(link_settings,
+                                            port_info->advertised_mode,
+                                            GET_ADVERTISED_MODE);
+
+       err = hinic3_get_link_status(nic_dev->hwdev, &link_status_up);
+       if (!err && link_status_up) {
+               link_settings->speed =
+                       port_info->speed < ARRAY_SIZE(hw_to_ethtool_speed) ?
+                       hw_to_ethtool_speed[port_info->speed] :
+                       (u32)SPEED_UNKNOWN;
+
+               link_settings->duplex = port_info->duplex;
+       } else {
+               link_settings->speed = (u32)SPEED_UNKNOWN;
+               link_settings->duplex = DUPLEX_UNKNOWN;
+       }
+}
+
+static void
+hinic3_link_port_type_set(struct hinic3_link_settings *link_settings,
+                         u8 port_type)
+{
+       switch (port_type) {
+       case MAG_CMD_WIRE_TYPE_ELECTRIC:
+               HINIC3_ADD_SUPPORTED_LINK_MODE(link_settings, MODE_TP);
+               HINIC3_ADD_ADVERTISED_LINK_MODE(link_settings, MODE_TP);
+               link_settings->port = PORT_TP;
+               break;
+
+       case MAG_CMD_WIRE_TYPE_AOC:
+       case MAG_CMD_WIRE_TYPE_MM:
+       case MAG_CMD_WIRE_TYPE_SM:
+               HINIC3_ADD_SUPPORTED_LINK_MODE(link_settings, MODE_FIBRE);
+               HINIC3_ADD_ADVERTISED_LINK_MODE(link_settings, MODE_FIBRE);
+               link_settings->port = PORT_FIBRE;
+               break;
+
+       case MAG_CMD_WIRE_TYPE_COPPER:
+               HINIC3_ADD_SUPPORTED_LINK_MODE(link_settings, MODE_FIBRE);
+               HINIC3_ADD_ADVERTISED_LINK_MODE(link_settings, MODE_FIBRE);
+               link_settings->port = PORT_DA;
+               break;
+
+       case MAG_CMD_WIRE_TYPE_BACKPLANE:
+               HINIC3_ADD_SUPPORTED_LINK_MODE(link_settings, MODE_Backplane);
+               HINIC3_ADD_ADVERTISED_LINK_MODE(link_settings, MODE_Backplane);
+               link_settings->port = PORT_NONE;
+               break;
+
+       default:
+               link_settings->port = PORT_OTHER;
+               break;
+       }
+}
+
+static int
+hinic3_get_link_pause_settings(struct net_device *netdev,
+                              struct hinic3_link_settings *link_settings)
+{
+       struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+       struct hinic3_nic_pause_config nic_pause = {};
+       int err;
+
+       err = hinic3_get_pause_info(nic_dev, &nic_pause);
+       if (err) {
+               netdev_err(netdev, "Failed to get pause param from hw\n");
+               return err;
+       }
+
+       HINIC3_ADD_SUPPORTED_LINK_MODE(link_settings, MODE_Pause);
+       if (nic_pause.rx_pause && nic_pause.tx_pause) {
+               HINIC3_ADD_ADVERTISED_LINK_MODE(link_settings, MODE_Pause);
+       } else if (nic_pause.tx_pause) {
+               HINIC3_ADD_ADVERTISED_LINK_MODE(link_settings,
+                                               MODE_Asym_Pause);
+       } else if (nic_pause.rx_pause) {
+               HINIC3_ADD_ADVERTISED_LINK_MODE(link_settings, MODE_Pause);
+               HINIC3_ADD_ADVERTISED_LINK_MODE(link_settings,
+                                               MODE_Asym_Pause);
+       }
+
+       return 0;
+}
+
+static int
+hinic3_get_link_settings(struct net_device *netdev,
+                        struct hinic3_link_settings *link_settings)
+{
+       struct hinic3_nic_dev *nic_dev = netdev_priv(netdev);
+       struct hinic3_nic_port_info port_info = {};
+       int err;
+
+       err = hinic3_get_port_info(nic_dev->hwdev, &port_info);
+       if (err) {
+               netdev_err(netdev, "Failed to get port info\n");
+               return err;
+       }
+
+       hinic3_link_speed_set(netdev, link_settings, &port_info);
+
+       hinic3_link_port_type_set(link_settings, port_info.port_type);
+
+       link_settings->autoneg = port_info.autoneg_state == PORT_CFG_AN_ON ?
+                                AUTONEG_ENABLE : AUTONEG_DISABLE;
+       if (port_info.autoneg_cap)
+               HINIC3_ADD_SUPPORTED_LINK_MODE(link_settings, MODE_Autoneg);
+       if (port_info.autoneg_state == PORT_CFG_AN_ON)
+               HINIC3_ADD_ADVERTISED_LINK_MODE(link_settings, MODE_Autoneg);
+
+       if (!HINIC3_IS_VF(nic_dev->hwdev)) {
+               err = hinic3_get_link_pause_settings(netdev, link_settings);
+               if (err)
+                       return err;
+       }
+
+       return 0;
+}
+
+static int
+hinic3_get_link_ksettings(struct net_device *netdev,
+                         struct ethtool_link_ksettings *link_settings)
+{
+       struct ethtool_link_settings *base = &link_settings->base;
+       struct hinic3_link_settings settings = {};
+       int err;
+
+       ethtool_link_ksettings_zero_link_mode(link_settings, supported);
+       ethtool_link_ksettings_zero_link_mode(link_settings, advertising);
+
+       err = hinic3_get_link_settings(netdev, &settings);
+       if (err)
+               return err;
+
+       bitmap_copy(link_settings->link_modes.supported, settings.supported,
+                   __ETHTOOL_LINK_MODE_MASK_NBITS);
+       bitmap_copy(link_settings->link_modes.advertising, settings.advertising,
+                   __ETHTOOL_LINK_MODE_MASK_NBITS);
+
+       base->autoneg = settings.autoneg;
+       base->speed = settings.speed;
+       base->duplex = settings.duplex;
+       base->port = settings.port;
+
+       return 0;
+}
+
+static const struct ethtool_ops hinic3_ethtool_ops = {
+       .supported_coalesce_params      = ETHTOOL_COALESCE_USECS |
+                                         ETHTOOL_COALESCE_PKT_RATE_RX_USECS,
+       .get_link_ksettings             = hinic3_get_link_ksettings,
+       .get_drvinfo                    = hinic3_get_drvinfo,
+       .get_msglevel                   = hinic3_get_msglevel,
+       .set_msglevel                   = hinic3_set_msglevel,
+       .get_link                       = ethtool_op_get_link,
+};
+
+void hinic3_set_ethtool_ops(struct net_device *netdev)
+{
+       netdev->ethtool_ops = &hinic3_ethtool_ops;
+}
index a6e4e996833434ee74d8a73025b3eeefb6d87e7e..0dbf9cbe7f9ab3049fffbb63342d08a47dd7d2a6 100644 (file)
@@ -580,3 +580,31 @@ int hinic3_clean_root_ctxt(struct hinic3_hwdev *hwdev)
 
        return 0;
 }
+
+#define HINIC3_FW_VER_TYPE_MPU  1
+
+int hinic3_get_mgmt_version(struct hinic3_hwdev *hwdev, u8 *mgmt_ver,
+                           u8 version_size)
+{
+       struct comm_cmd_get_fw_version fw_ver = {};
+       struct mgmt_msg_params msg_params = {};
+       int err;
+
+       fw_ver.fw_type = HINIC3_FW_VER_TYPE_MPU;
+
+       mgmt_msg_params_init_default(&msg_params, &fw_ver, sizeof(fw_ver));
+
+       err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_COMM,
+                                      COMM_CMD_GET_FW_VERSION, &msg_params);
+
+       if (err || fw_ver.head.status) {
+               dev_err(hwdev->dev,
+                       "Failed to get fw version, err: %d, status: 0x%x\n",
+                       err, fw_ver.head.status);
+               return -EFAULT;
+       }
+
+       snprintf(mgmt_ver, version_size, "%s", fw_ver.ver);
+
+       return 0;
+}
index 8e4737c486b7ff0b0ccbd8625f47131e823718d4..e672f9af5cb142a66daaf912b6866fe346863332 100644 (file)
@@ -49,5 +49,7 @@ void hinic3_sync_time_to_fw(struct hinic3_hwdev *hwdev);
 int hinic3_set_root_ctxt(struct hinic3_hwdev *hwdev, u32 rq_depth, u32 sq_depth,
                         int rx_buf_sz);
 int hinic3_clean_root_ctxt(struct hinic3_hwdev *hwdev);
+int hinic3_get_mgmt_version(struct hinic3_hwdev *hwdev, u8 *mgmt_ver,
+                           u8 version_size);
 
 #endif
index 329a9c464ff95bac08c8c40f9d36d653daf5149c..cfc9daa3034feb8c2e696253a8d958d049a9cc8c 100644 (file)
@@ -114,6 +114,7 @@ enum comm_cmd {
        COMM_CMD_SET_DMA_ATTR            = 25,
 
        /* Commands for obtaining information */
+       COMM_CMD_GET_FW_VERSION          = 60,
        COMM_CMD_SYNC_TIME               = 62,
        COMM_CMD_SEND_BDF_INFO           = 64,
 };
@@ -275,6 +276,17 @@ struct comm_cmd_bdf_info {
        u8                   rsvd2[5];
 };
 
+#define COMM_FW_VERSION_LEN       16
+#define COMM_FW_COMPILE_TIME_LEN  20
+struct comm_cmd_get_fw_version {
+       struct mgmt_msg_head head;
+
+       u16                  fw_type;
+       u16                  rsvd1;
+       u8                   ver[COMM_FW_VERSION_LEN];
+       u8                   time[COMM_FW_COMPILE_TIME_LEN];
+};
+
 /* Services supported by HW. HW uses these values when delivering events.
  * HW supports multiple services that are not yet supported by driver
  * (e.g. RoCE).
index 6039fcf3c1dd1a5048254751290c1fa2ee1983b8..0a888fe4c975b2bd3fc9d8408eeb8895e7bcd0a8 100644 (file)
@@ -18,6 +18,7 @@
 
 #define HINIC3_NIC_DRV_DESC  "Intelligent Network Interface Card Driver"
 
+#define HINIC3_DEFAULT_MSG_ENABLE  (NETIF_MSG_DRV | NETIF_MSG_LINK)
 #define HINIC3_RX_BUF_LEN          2048
 #define HINIC3_LRO_REPLENISH_THLD  256
 #define HINIC3_NIC_DEV_WQ_NAME     "hinic3_nic_dev_wq"
@@ -143,6 +144,7 @@ static int hinic3_init_nic_dev(struct net_device *netdev,
        nic_dev->hwdev = hwdev;
        nic_dev->pdev = pdev;
 
+       nic_dev->msg_enable = HINIC3_DEFAULT_MSG_ENABLE;
        nic_dev->rx_buf_len = HINIC3_RX_BUF_LEN;
        nic_dev->lro_replenish_thld = HINIC3_LRO_REPLENISH_THLD;
        nic_dev->vlan_bitmap = kzalloc(HINIC3_VLAN_BITMAP_SIZE(nic_dev),
@@ -241,6 +243,7 @@ static void hinic3_sw_uninit(struct net_device *netdev)
 static void hinic3_assign_netdev_ops(struct net_device *netdev)
 {
        hinic3_set_netdev_ops(netdev);
+       hinic3_set_ethtool_ops(netdev);
 }
 
 static void netdev_feature_init(struct net_device *netdev)
index c0c87a8c219895fa6e74d1a7e39cd460275283ad..c5bca3c4af961e445ea340a38a559da15aadaa68 100644 (file)
@@ -183,7 +183,18 @@ struct l2nic_cmd_lro_timer {
 /* IEEE 802.1Qaz std */
 #define L2NIC_DCB_COS_MAX     0x8
 
-struct l2nic_cmd_set_rss_ctx_tbl {
+struct l2nic_cmd_pause_config {
+       struct mgmt_msg_head msg_head;
+       u8                   port_id;
+       u8                   opcode;
+       u16                  rsvd1;
+       u8                   auto_neg;
+       u8                   rx_pause;
+       u8                   tx_pause;
+       u8                   rsvd2[5];
+};
+
+struct l2nic_cmd_rss_ctx_tbl {
        struct mgmt_msg_head msg_head;
        u16                  func_id;
        u16                  rsvd1;
@@ -238,6 +249,7 @@ enum l2nic_cmd {
        L2NIC_CMD_CFG_RSS_HASH_KEY    = 63,
        L2NIC_CMD_CFG_RSS_HASH_ENGINE = 64,
        L2NIC_CMD_SET_RSS_CTX_TBL     = 65,
+       L2NIC_CMD_CFG_PAUSE_INFO      = 101,
        L2NIC_CMD_QOS_DCB_STATE       = 110,
        L2NIC_CMD_FORCE_PKT_DROP      = 113,
        L2NIC_CMD_MAX                 = 256,
@@ -259,6 +271,8 @@ enum l2nic_ucode_cmd {
 enum mag_cmd {
        MAG_CMD_SET_PORT_ENABLE = 6,
        MAG_CMD_GET_LINK_STATUS = 7,
+
+       MAG_CMD_GET_PORT_INFO   = 153,
 };
 
 /* firmware also use this cmd report link event to driver */
index 44abccf9cb29f3e9b6c87527991dedbd6a0298c1..de5a7984d2cb957e62ce4bc148a61daadffc73e2 100644 (file)
@@ -639,6 +639,39 @@ int hinic3_get_link_status(struct hinic3_hwdev *hwdev, bool *link_status_up)
        return 0;
 }
 
+int hinic3_get_port_info(struct hinic3_hwdev *hwdev,
+                        struct hinic3_nic_port_info *port_info)
+{
+       struct mag_cmd_get_port_info port_msg = {};
+       struct mgmt_msg_params msg_params = {};
+       int err;
+
+       port_msg.port_id = hinic3_physical_port_id(hwdev);
+
+       mgmt_msg_params_init_default(&msg_params, &port_msg, sizeof(port_msg));
+
+       err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_HILINK,
+                                      MAG_CMD_GET_PORT_INFO, &msg_params);
+
+       if (err || port_msg.head.status) {
+               dev_err(hwdev->dev,
+                       "Failed to get port info, err: %d, status: 0x%x\n",
+                       err, port_msg.head.status);
+               return -EFAULT;
+       }
+
+       port_info->autoneg_cap = port_msg.an_support;
+       port_info->autoneg_state = port_msg.an_en;
+       port_info->duplex = port_msg.duplex;
+       port_info->port_type = port_msg.wire_type;
+       port_info->speed = port_msg.speed;
+       port_info->fec = port_msg.fec;
+       port_info->supported_mode = port_msg.supported_mode;
+       port_info->advertised_mode = port_msg.advertised_mode;
+
+       return 0;
+}
+
 int hinic3_set_vport_enable(struct hinic3_hwdev *hwdev, u16 func_id,
                            bool enable)
 {
@@ -661,3 +694,47 @@ int hinic3_set_vport_enable(struct hinic3_hwdev *hwdev, u16 func_id,
 
        return 0;
 }
+
+static int hinic3_cfg_hw_pause(struct hinic3_hwdev *hwdev, u8 opcode,
+                              struct hinic3_nic_pause_config *nic_pause)
+{
+       struct l2nic_cmd_pause_config pause_info = {};
+       struct mgmt_msg_params msg_params = {};
+       int err;
+
+       pause_info.port_id = hinic3_physical_port_id(hwdev);
+       pause_info.opcode = opcode;
+       if (opcode == MGMT_MSG_CMD_OP_SET) {
+               pause_info.auto_neg = nic_pause->auto_neg;
+               pause_info.rx_pause = nic_pause->rx_pause;
+               pause_info.tx_pause = nic_pause->tx_pause;
+       }
+
+       mgmt_msg_params_init_default(&msg_params, &pause_info,
+                                    sizeof(pause_info));
+
+       err = hinic3_send_mbox_to_mgmt(hwdev, MGMT_MOD_L2NIC,
+                                      L2NIC_CMD_CFG_PAUSE_INFO, &msg_params);
+
+       if (err || pause_info.msg_head.status) {
+               dev_err(hwdev->dev, "Failed to %s pause info, err: %d, status: 0x%x\n",
+                       opcode == MGMT_MSG_CMD_OP_SET ? "set" : "get",
+                       err, pause_info.msg_head.status);
+               return -EFAULT;
+       }
+
+       if (opcode == MGMT_MSG_CMD_OP_GET) {
+               nic_pause->auto_neg = pause_info.auto_neg;
+               nic_pause->rx_pause = pause_info.rx_pause;
+               nic_pause->tx_pause = pause_info.tx_pause;
+       }
+
+       return 0;
+}
+
+int hinic3_get_pause_info(struct hinic3_nic_dev *nic_dev,
+                         struct hinic3_nic_pause_config *nic_pause)
+{
+       return hinic3_cfg_hw_pause(nic_dev->hwdev, MGMT_MSG_CMD_OP_GET,
+                                  nic_pause);
+}
index c32eaa886e179027b82913e08f2fc72ebb95f9f9..5d52202a8d4eb18818cf5ca05962d27f143d5017 100644 (file)
@@ -6,6 +6,7 @@
 
 #include <linux/types.h>
 
+#include "hinic3_hwif.h"
 #include "hinic3_hw_intf.h"
 #include "hinic3_mgmt_interface.h"
 
@@ -35,6 +36,49 @@ struct hinic3_sq_attr {
        u64 ci_dma_base;
 };
 
+enum mag_cmd_port_an {
+       PORT_CFG_AN_ON  = 1,
+};
+
+/* mag supported/advertised link mode bitmap */
+enum mag_cmd_link_mode {
+       LINK_MODE_GE            = 0,
+       LINK_MODE_10GE_BASE_R   = 1,
+       LINK_MODE_25GE_BASE_R   = 2,
+       LINK_MODE_40GE_BASE_R4  = 3,
+       LINK_MODE_50GE_BASE_R   = 4,
+       LINK_MODE_50GE_BASE_R2  = 5,
+       LINK_MODE_100GE_BASE_R  = 6,
+       LINK_MODE_100GE_BASE_R2 = 7,
+       LINK_MODE_100GE_BASE_R4 = 8,
+       LINK_MODE_200GE_BASE_R2 = 9,
+       LINK_MODE_200GE_BASE_R4 = 10,
+       LINK_MODE_MAX_NUMBERS,
+
+       LINK_MODE_UNKNOWN       = 0xFFFF
+};
+
+struct mag_cmd_get_port_info {
+       struct mgmt_msg_head head;
+
+       u8                   port_id;
+       u8                   rsvd0[3];
+
+       u8                   wire_type;
+       u8                   an_support;
+       u8                   an_en;
+       u8                   duplex;
+
+       u8                   speed;
+       u8                   fec;
+       u8                   lanes;
+       u8                   rsvd1;
+
+       u32                  supported_mode;
+       u32                  advertised_mode;
+       u8                   rsvd2[8];
+};
+
 #define MAG_CMD_PORT_DISABLE    0x0
 #define MAG_CMD_TX_ENABLE       0x1
 #define MAG_CMD_RX_ENABLE       0x2
@@ -52,6 +96,39 @@ struct mag_cmd_set_port_enable {
        u8                   rsvd1[3];
 };
 
+/* xsfp wire type, refers to cmis protocol definition */
+enum mag_wire_type {
+       MAG_CMD_WIRE_TYPE_UNKNOWN   = 0x0,
+       MAG_CMD_WIRE_TYPE_MM        = 0x1,
+       MAG_CMD_WIRE_TYPE_SM        = 0x2,
+       MAG_CMD_WIRE_TYPE_COPPER    = 0x3,
+       MAG_CMD_WIRE_TYPE_ACC       = 0x4,
+       MAG_CMD_WIRE_TYPE_BASET     = 0x5,
+       MAG_CMD_WIRE_TYPE_AOC       = 0x40,
+       MAG_CMD_WIRE_TYPE_ELECTRIC  = 0x41,
+       MAG_CMD_WIRE_TYPE_BACKPLANE = 0x42
+};
+
+#define XSFP_INFO_MAX_SIZE    640
+struct mag_cmd_get_xsfp_info {
+       struct mgmt_msg_head head;
+
+       u8                   port_id;
+       u8                   wire_type;
+       u16                  out_len;
+       u32                  rsvd;
+       u8                   sfp_info[XSFP_INFO_MAX_SIZE];
+};
+
+struct mag_cmd_get_xsfp_present {
+       struct mgmt_msg_head head;
+
+       u8                   port_id;
+       /* 0:present, 1:absent */
+       u8                   abs_status;
+       u8                   rsvd[2];
+};
+
 enum link_err_type {
        LINK_ERR_MODULE_UNRECOGENIZED,
        LINK_ERR_NUM,
@@ -69,6 +146,34 @@ struct hinic3_port_module_event {
        enum link_err_type          err_type;
 };
 
+struct hinic3_nic_port_info {
+       u8  port_type;
+       u8  autoneg_cap;
+       u8  autoneg_state;
+       u8  duplex;
+       u8  speed;
+       u8  fec;
+       u32 supported_mode;
+       u32 advertised_mode;
+};
+
+struct hinic3_nic_pause_config {
+       u8 auto_neg;
+       u8 rx_pause;
+       u8 tx_pause;
+};
+
+struct hinic3_nic_cfg {
+       /* Valid when pfc is disabled */
+       bool                           pause_set;
+       struct hinic3_nic_pause_config nic_pause;
+
+       u8                             pfc_en;
+       u8                             pfc_bitmap;
+
+       struct hinic3_nic_port_info    port_info;
+};
+
 int hinic3_get_nic_feature_from_hw(struct hinic3_nic_dev *nic_dev);
 int hinic3_set_nic_feature_to_hw(struct hinic3_nic_dev *nic_dev);
 bool hinic3_test_support(struct hinic3_nic_dev *nic_dev,
@@ -100,9 +205,14 @@ int hinic3_set_rx_mode(struct hinic3_hwdev *hwdev, u32 rx_mode);
 int hinic3_sync_dcb_state(struct hinic3_hwdev *hwdev, u8 op_code, u8 state);
 int hinic3_set_port_enable(struct hinic3_hwdev *hwdev, bool enable);
 int hinic3_get_link_status(struct hinic3_hwdev *hwdev, bool *link_status_up);
+int hinic3_get_port_info(struct hinic3_hwdev *hwdev,
+                        struct hinic3_nic_port_info *port_info);
 int hinic3_set_vport_enable(struct hinic3_hwdev *hwdev, u16 func_id,
                            bool enable);
 int hinic3_add_vlan(struct hinic3_hwdev *hwdev, u16 vlan_id, u16 func_id);
 int hinic3_del_vlan(struct hinic3_hwdev *hwdev, u16 vlan_id, u16 func_id);
 
+int hinic3_get_pause_info(struct hinic3_nic_dev *nic_dev,
+                         struct hinic3_nic_pause_config *nic_pause);
+
 #endif
index 29189241f446ca2fdd97dbe380f9abcd3acfcc20..9502293ff710c6bb26568e4eb9fab9bc0d423827 100644 (file)
@@ -101,6 +101,7 @@ struct hinic3_nic_dev {
        struct hinic3_hwdev             *hwdev;
        struct hinic3_nic_io            *nic_io;
 
+       u32                             msg_enable;
        u16                             max_qps;
        u16                             rx_buf_len;
        u32                             lro_replenish_thld;
@@ -148,4 +149,6 @@ void hinic3_qps_irq_uninit(struct net_device *netdev);
 void hinic3_set_rx_mode_work(struct work_struct *work);
 void hinic3_clean_mac_list_filter(struct net_device *netdev);
 
+void hinic3_set_ethtool_ops(struct net_device *netdev);
+
 #endif
index 4ff1b2f79838aab275b55467347c8f131fc101dd..25db74d8c7ddb778a02fa8e565f5de4ddad87db1 100644 (file)
@@ -132,7 +132,7 @@ static int hinic3_rss_set_indir_tbl(struct hinic3_hwdev *hwdev,
 static int hinic3_set_rss_type(struct hinic3_hwdev *hwdev,
                               struct hinic3_rss_type rss_type)
 {
-       struct l2nic_cmd_set_rss_ctx_tbl ctx_tbl = {};
+       struct l2nic_cmd_rss_ctx_tbl ctx_tbl = {};
        struct mgmt_msg_params msg_params = {};
        u32 ctx;
        int err;