[PATCH v1 10/17] net/hinic3: add SP560 NIC type identification and handling

Jiacheng Ye yejiacheng4 at h-partners.com
Sat Oct 10 16:45:01 CEST 2026


Identify the new SP560 NIC through its PCI device ID and select NIC-type
specific operations, and add 400G/800G line rate support. At init, read
the receive WQE type from the kernel module parameter to decide whether
the SP560 uses the normal or compact CQE data path.

Signed-off-by: Jiacheng Ye <yejiacheng4 at h-partners.com>
---
 doc/guides/rel_notes/release_26_11.rst     |   1 +
 drivers/net/hinic3/base/hinic3_csr.h       |   8 +-
 drivers/net/hinic3/base/hinic3_hwif.c      |   4 +-
 drivers/net/hinic3/base/hinic3_nic_cfg.h   |   4 +
 drivers/net/hinic3/base/hinic3_nic_event.c |  72 +++++++++++--
 drivers/net/hinic3/hinic3_ethdev.c         | 111 ++++++++++++++++++++-
 drivers/net/hinic3/hinic3_rx.c             |  19 ++++
 7 files changed, 206 insertions(+), 13 deletions(-)

diff --git a/doc/guides/rel_notes/release_26_11.rst b/doc/guides/rel_notes/release_26_11.rst
index 80530ac25d..2e8e1e082a 100644
--- a/doc/guides/rel_notes/release_26_11.rst
+++ b/doc/guides/rel_notes/release_26_11.rst
@@ -98,6 +98,7 @@ New Features
   * Added FEC mode get and set support.
   * Added Class of Service (COS) mask and map support.
   * Updated RSS hash type configuration.
+  * Added support for the SP560 NIC.
 
 * **Updated Intel iavf driver.**
 
diff --git a/drivers/net/hinic3/base/hinic3_csr.h b/drivers/net/hinic3/base/hinic3_csr.h
index eceb34e9fd..fba2f6838a 100644
--- a/drivers/net/hinic3/base/hinic3_csr.h
+++ b/drivers/net/hinic3/base/hinic3_csr.h
@@ -8,13 +8,17 @@
 #define PCI_VENDOR_ID_HUAWEI	0x19e5
 
 #define HINIC3_DEV_ID_SP620	0x0222
-#define HINIC3_DEV_ID_VF_SP620	0x375F
+#define HINIC3_DEV_ID_VF_SP620	0x375f
 
 #define HINIC3_DEV_ID_SP230	0x0229
-#define HINIC3_DEV_ID_VF_SP230	0x3750
+#define HINIC3_DEV_ID_VF_SP230	0x022a
 
 #define HINIC3_DEV_ID_SP920	0x0224
 
+#define HINIC3_DEV_ID_SP560		0x0230
+#define HINIC3_DEV_ID_VF_SP560		0x0231
+#define HINIC3_DEV_ID_HYPER_VF_SP560	0x0232
+
 /*
  * Bit30/bit31 for bar index flag.
  * 00: bar0
diff --git a/drivers/net/hinic3/base/hinic3_hwif.c b/drivers/net/hinic3/base/hinic3_hwif.c
index c82b223fa0..af1dddabf4 100644
--- a/drivers/net/hinic3/base/hinic3_hwif.c
+++ b/drivers/net/hinic3/base/hinic3_hwif.c
@@ -141,7 +141,9 @@
 static inline bool hinic3_is_vf_dev(const struct rte_pci_device *pdev)
 {
 	return pdev->id.device_id == HINIC3_DEV_ID_VF_SP620 ||
-	       pdev->id.device_id == HINIC3_DEV_ID_VF_SP230;
+	       pdev->id.device_id == HINIC3_DEV_ID_VF_SP230 ||
+	       pdev->id.device_id == HINIC3_DEV_ID_VF_SP560 ||
+	       pdev->id.device_id == HINIC3_DEV_ID_HYPER_VF_SP560;
 }
 
 uint32_t
diff --git a/drivers/net/hinic3/base/hinic3_nic_cfg.h b/drivers/net/hinic3/base/hinic3_nic_cfg.h
index ef139b25b9..0de8179c19 100644
--- a/drivers/net/hinic3/base/hinic3_nic_cfg.h
+++ b/drivers/net/hinic3/base/hinic3_nic_cfg.h
@@ -127,6 +127,8 @@ enum nic_speed_level {
 	LINK_SPEED_50GB,
 	LINK_SPEED_100GB,
 	LINK_SPEED_200GB,
+	LINK_SPEED_400GB,
+	LINK_SPEED_800GB,
 	LINK_SPEED_LEVELS,
 };
 
@@ -181,6 +183,8 @@ enum mag_cmd_port_speed {
 	PORT_SPEED_50GB    = 7,
 	PORT_SPEED_100GB   = 8,
 	PORT_SPEED_200GB   = 9,
+	PORT_SPEED_400GB   = 10,
+	PORT_SPEED_800GB   = 11,
 	PORT_SPEED_UNKNOWN
 };
 
diff --git a/drivers/net/hinic3/base/hinic3_nic_event.c b/drivers/net/hinic3/base/hinic3_nic_event.c
index 2f2d6aa75b..131acc47dd 100644
--- a/drivers/net/hinic3/base/hinic3_nic_event.c
+++ b/drivers/net/hinic3/base/hinic3_nic_event.c
@@ -15,6 +15,7 @@ static const char *g_hw_to_char_fec[HILINK_FEC_MAX_TYPE] = {
 static const char *g_hw_to_speed_info[PORT_SPEED_UNKNOWN] = {
 	"not set", "10MB", "100MB", "1GB",   "10GB",
 	"25GB",	   "40GB", "50GB",  "100GB", "200GB",
+	"400GB",   "800GB",
 };
 static const char *g_hw_to_an_state_info[PORT_CFG_AN_OFF + 1] = {
 	"not set", "on", "off",
@@ -25,6 +26,37 @@ struct port_type_table {
 	const char *port_type_name;
 };
 
+/*
+ * Hardware cable speed modes.
+ *
+ * The firmware reports the cable speed mode as a uint8_t value. Speeds whose raw
+ * Gbps value cannot be represented in 8 bits (notably 400G and 800G) are
+ * therefore encoded with indirection codes instead, and must be translated
+ * back to their real speeds through speed_mode_map[] below.
+ */
+enum {
+	HW_CABLE_SPEED_MODE_200G = 200,
+	HW_CABLE_SPEED_MODE_400G = 201,
+	HW_CABLE_SPEED_MODE_800G = 202,
+};
+
+/* Real cable speeds in Gbps, as exposed to the upper layer. */
+enum {
+	REAL_CABLE_SPEED_400G    = 400,
+	REAL_CABLE_SPEED_800G    = 800,
+};
+
+/* Mapping between a cable speed mode (uint8_t) and its real speed in Gbps. */
+struct speed_mode_map_entry {
+	uint8_t speed_mode;
+	uint32_t real_speed;
+};
+
+static const struct speed_mode_map_entry speed_mode_map[] = {
+	{HW_CABLE_SPEED_MODE_400G, REAL_CABLE_SPEED_400G},
+	{HW_CABLE_SPEED_MODE_800G, REAL_CABLE_SPEED_800G},
+};
+
 void
 hinic3_get_link_port_info(struct hinic3_hwdev *hwdev, uint8_t link_state,
 	      struct rte_eth_link *link)
@@ -35,6 +67,7 @@ hinic3_get_link_port_info(struct hinic3_hwdev *hwdev, uint8_t link_state,
 		RTE_ETH_SPEED_NUM_10G,	RTE_ETH_SPEED_NUM_25G,
 		RTE_ETH_SPEED_NUM_40G,	RTE_ETH_SPEED_NUM_50G,
 		RTE_ETH_SPEED_NUM_100G, RTE_ETH_SPEED_NUM_200G,
+		RTE_ETH_SPEED_NUM_400G, RTE_ETH_SPEED_NUM_800G
 	};
 	struct nic_port_info port_info = {0};
 	int err;
@@ -53,6 +86,13 @@ hinic3_get_link_port_info(struct hinic3_hwdev *hwdev, uint8_t link_state,
 			link->link_duplex = RTE_ETH_LINK_FULL_DUPLEX;
 			link->link_autoneg = RTE_ETH_LINK_FIXED;
 		} else {
+			if (port_info.speed >= LINK_SPEED_LEVELS) {
+				PMD_DRV_LOG(WARNING,
+					"Invalid port speed %u reported by firmware, reset to %u",
+					port_info.speed,
+					port_info.speed % LINK_SPEED_LEVELS);
+			}
+
 			link->link_speed =
 				port_speed[port_info.speed % LINK_SPEED_LEVELS];
 			link->link_duplex = port_info.duplex;
@@ -176,6 +216,22 @@ get_port_temperature_power(struct mag_cmd_event_port_info *info, char *str)
 	return 0;
 }
 
+static uint32_t
+get_real_cable_speed(uint8_t speed)
+{
+	uint32_t i;
+
+	if (speed <= HW_CABLE_SPEED_MODE_200G)
+		return speed;
+	for (i = 0; i < RTE_DIM(speed_mode_map); i++) {
+		if (speed_mode_map[i].speed_mode == speed)
+			return speed_mode_map[i].real_speed;
+	}
+
+	PMD_DRV_LOG(ERR, "unsupported cable speed mode: 0x%x", speed);
+	return speed;
+}
+
 static void
 print_cable_info(struct mag_cmd_event_port_info *port_info)
 {
@@ -201,9 +257,9 @@ print_cable_info(struct mag_cmd_event_port_info *port_info)
 	memcpy(vendor, port_info->vendor_name,
 	       sizeof(port_info->vendor_name));
 	snprintf(str, CAP_INFO_MAX_LEN - 1,
-		     "Vendor: %s, %s, length: %um, max_speed: %uGbps",
-		     vendor, port_type, port_info->cable_length,
-		     port_info->max_speed);
+	     "Vendor: %s, %s, length: %um, max_speed: %uGbps",
+	     vendor, port_type, port_info->cable_length,
+	     get_real_cable_speed(port_info->max_speed));
 
 	if (port_info->port_type == LINK_PORT_OPTICAL_MM ||
 	    port_info->port_type == LINK_PORT_AOC) {
@@ -282,9 +338,13 @@ port_info_event_printf(struct hinic3_hwdev *hwdev, struct hinic3_handler_info *h
 	struct mag_cmd_event_port_info *port_info = handler_info->buf_in;
 	((struct mag_cmd_event_port_info *)(handler_info->buf_out))->head.status = 0;
 	enum hinic3_nic_event_type type = port_info->event_type;
-	if (type < RTE_ETH_LINK_DOWN || type > RTE_ETH_LINK_UP) {
-		PMD_DRV_LOG(ERR, "Invalid hilink info report, type: %d", type);
-		return;
+	struct hinic3_nic_dev *nic_dev = ((struct hinic3_hwdev *)hwdev)->dev_handle;
+
+	if (HINIC3_IS_SP620_NIC(nic_dev)) {
+		if (type < RTE_ETH_LINK_DOWN || type > RTE_ETH_LINK_UP) {
+			PMD_DRV_LOG(ERR, "Invalid hilink info report, type: %d", type);
+			return;
+		}
 	}
 
 	print_port_info(hwdev, port_info, type);
diff --git a/drivers/net/hinic3/hinic3_ethdev.c b/drivers/net/hinic3/hinic3_ethdev.c
index 9adfe7db41..603330846e 100644
--- a/drivers/net/hinic3/hinic3_ethdev.c
+++ b/drivers/net/hinic3/hinic3_ethdev.c
@@ -472,6 +472,67 @@ hinic3_pf_get_default_cos(struct hinic3_hwdev *hwdev, uint8_t *cos_id)
 	return 0;
 }
 
+static int
+hinic3_parse_sysfs_value(const char *filename, unsigned long *val)
+{
+	FILE *f;
+	char buf[BUFSIZ];
+	char *end = NULL;
+
+	f = fopen(filename, "r");
+	if (f == NULL) {
+		PMD_DRV_LOG(ERR, "Cannot open sysfs value %s", filename);
+		return -1;
+	}
+
+	if (fgets(buf, sizeof(buf), f) == NULL) {
+		PMD_DRV_LOG(ERR, "Cannot read sysfs value %s", filename);
+		fclose(f);
+		return -1;
+	}
+	*val = strtoul(buf, &end, 0);
+	if ((buf[0] == '\0') || end == NULL || (*end != '\n')) {
+		PMD_DRV_LOG(ERR, "Cannot parse sysfs value %s", filename);
+		fclose(f);
+		return -1;
+	}
+	fclose(f);
+	return 0;
+}
+
+/*
+ * Try to read sysfs parameter from kernel module.
+ * SP620 NIC uses "hinic3" kernel driver, other NICs (e.g. BP/SP560/SP230)
+ * use "hinic5" kernel driver.
+ * The module name could be either "driver_name" or "driver_name_nic".
+ * Returns 0 on success, -1 on failure.
+ */
+static int
+hinic3_read_module_param(struct hinic3_nic_dev *nic_dev, const char *param_name,
+			 unsigned long *val)
+{
+	const char *driver_name = HINIC3_IS_SP620_NIC(nic_dev) ?
+				  "hinic3" : "hinic5";
+	char path[PATH_MAX];
+	int ret;
+
+	snprintf(path, sizeof(path), "/sys/module/%s/parameters/%s",
+		 driver_name, param_name);
+	ret = hinic3_parse_sysfs_value(path, val);
+	if (ret == 0)
+		return 0;
+
+	snprintf(path, sizeof(path), "/sys/module/%s_nic/parameters/%s",
+		 driver_name, param_name);
+	ret = hinic3_parse_sysfs_value(path, val);
+	if (ret == 0)
+		return 0;
+
+	PMD_DRV_LOG(ERR, "Failed to read parameter %s from module %s or %s_nic",
+		    param_name, driver_name, driver_name);
+	return -1;
+}
+
 static void
 hinic3_get_cos_mask(struct hinic3_hwdev *hwdev, uint8_t *cos_mask)
 {
@@ -539,6 +600,26 @@ hinic3_init_default_cos(struct hinic3_nic_dev *nic_dev)
 	return 0;
 }
 
+static enum nic_type
+hinic3_get_nic_type(struct rte_pci_device *pci_dev)
+{
+	switch (pci_dev->id.device_id) {
+	case HINIC3_DEV_ID_SP620:
+	case HINIC3_DEV_ID_VF_SP620:
+	case HINIC3_DEV_ID_SP920: /* SP920 is the DPU card of SP620. */
+		return NIC_SP620;
+	case HINIC3_DEV_ID_SP560:
+	case HINIC3_DEV_ID_VF_SP560:
+	case HINIC3_DEV_ID_HYPER_VF_SP560:
+		return NIC_SP560;
+	case HINIC3_DEV_ID_SP230:
+	case HINIC3_DEV_ID_VF_SP230:
+		return NIC_SP230;
+	default:
+		return NIC_UNKNOWN;
+	}
+}
+
 /**
  * Get cmdq ops for the given NIC device.
  *
@@ -551,10 +632,18 @@ hinic3_init_default_cos(struct hinic3_nic_dev *nic_dev)
 const struct hinic3_nic_cmdq_ops *
 hinic3_cmdq_get_ops(struct hinic3_nic_dev *nic_dev)
 {
-	if (nic_dev->feature_cap & NIC_F_HTN_CMDQ)
-		return &hinic3_htn_cmdq_ops;
-	else
-		return &hinic3_stn_cmdq_ops;
+	static const struct hinic3_nic_cmdq_ops *cmdq_ops[] = {
+		[NIC_SP620]  = &hinic3_stn_cmdq_ops,
+		[NIC_SP560]  = &hinic3_stn_cmdq_ops,
+		[NIC_SP230]  = &hinic3_htn_cmdq_ops,
+		[NIC_UNKNOWN] = &hinic3_stn_cmdq_ops,
+	};
+	static const struct hinic3_nic_cmdq_ops *cached_ops;
+
+	if (cached_ops == NULL)
+		cached_ops = cmdq_ops[nic_dev->nic_type];
+
+	return cached_ops;
 }
 
 /**
@@ -3652,6 +3741,7 @@ hinic3_func_init(struct rte_eth_dev *eth_dev)
 	struct hinic3_tcam_info *tcam_info = NULL;
 	struct hinic3_nic_dev *nic_dev = NULL;
 	struct rte_pci_device *pci_dev = NULL;
+	unsigned long compact_cqe = 0;
 	int err;
 
 	pci_dev = RTE_CLASS_TO_BUS_DEVICE(eth_dev, *pci_dev);
@@ -3666,6 +3756,7 @@ hinic3_func_init(struct rte_eth_dev *eth_dev)
 
 	nic_dev = HINIC3_ETH_DEV_TO_PRIVATE_NIC_DEV(eth_dev);
 	memset(nic_dev, 0, sizeof(*nic_dev));
+	nic_dev->nic_type = hinic3_get_nic_type(pci_dev);
 	snprintf(nic_dev->dev_name, sizeof(nic_dev->dev_name),
 		"dbdf-%.4x:%.2x:%.2x.%x", pci_dev->addr.domain,
 		pci_dev->addr.bus, pci_dev->addr.devid,
@@ -3750,6 +3841,15 @@ hinic3_func_init(struct rte_eth_dev *eth_dev)
 
 	hinic3_nic_tx_rx_ops_init(nic_dev);
 
+	/* Read wqe type from kernel parameter. */
+	if (hinic3_read_module_param(nic_dev, "rq_wqe_type", &compact_cqe) != 0) {
+		err = -EINVAL;
+		goto get_cap_fail;
+	}
+
+	if (compact_cqe == 1)
+		nic_dev->feature_cap &= ~(NIC_F_RX_SW_COMPACT_CQE | NIC_F_RX_HW_COMPACT_CQE);
+
 	err = hinic3_init_sw_rxtxqs(nic_dev);
 	if (err) {
 		PMD_DRV_LOG(ERR, "Init sw rxqs or txqs failed, dev_name: %s",
@@ -3899,6 +3999,9 @@ static const struct rte_pci_id pci_id_hinic3_map[] = {
 	{RTE_PCI_DEVICE(PCI_VENDOR_ID_HUAWEI, HINIC3_DEV_ID_VF_SP230)},
 
 	{RTE_PCI_DEVICE(PCI_VENDOR_ID_HUAWEI, HINIC3_DEV_ID_SP920)},
+	{RTE_PCI_DEVICE(PCI_VENDOR_ID_HUAWEI, HINIC3_DEV_ID_SP560)},
+	{RTE_PCI_DEVICE(PCI_VENDOR_ID_HUAWEI, HINIC3_DEV_ID_VF_SP560)},
+	{RTE_PCI_DEVICE(PCI_VENDOR_ID_HUAWEI, HINIC3_DEV_ID_HYPER_VF_SP560)},
 
 	{.vendor_id = 0},
 };
diff --git a/drivers/net/hinic3/hinic3_rx.c b/drivers/net/hinic3/hinic3_rx.c
index 57e50590cd..493855b431 100644
--- a/drivers/net/hinic3/hinic3_rx.c
+++ b/drivers/net/hinic3/hinic3_rx.c
@@ -634,6 +634,25 @@ hinic3_poll_integrated_cqe_rq_empty(struct hinic3_rxq *rxq)
 	rq_ci.dw1.value = hinic3_hw_cpu32(val);
 	hw_ci = rq_ci.dw1.bs.hw_ci;
 
+	if (HINIC3_IS_SP560_NIC(rxq->nic_dev)) {
+		uint16_t sw_pi = hinic3_get_rq_local_pi(rxq);
+		uint64_t end;
+
+		end = cycles + msecs_to_cycles(HINIC3_FLUSH_QUEUE_TIMEOUT);
+		do {
+			val = rte_read32(&rxq->rq_ci->dw1.value);
+			rq_ci.dw1.value = hinic3_hw_cpu32(val);
+			hw_ci = rq_ci.dw1.bs.hw_ci;
+			if (sw_pi == hw_ci)
+				break;
+
+			rte_delay_us(1);
+		} while (time_before(cycles, end));
+
+		if (sw_pi != hw_ci)
+			return -EFAULT;
+	}
+
 	while (sw_ci != hw_ci) {
 		rx_info = &rxq->rx_info[sw_ci];
 		rte_pktmbuf_free(rx_info->mbuf);
-- 
2.33.0




More information about the dev mailing list