Netdev List
 help / color / mirror / Atom feed
* [PATCH net-next v9 0/2] Add ZTE DingHai Ethernet PF driver
@ 2026-07-20 10:06 han.junyang
  2026-07-20 10:10 ` [PATCH net-next v9 1/2] dinghai: add ZTE network driver support han.junyang
  2026-07-20 10:14 ` [PATCH net-next v9 2/2] dinghai: add hardware register access and PCI capability scanning han.junyang
  0 siblings, 2 replies; 3+ messages in thread
From: han.junyang @ 2026-07-20 10:06 UTC (permalink / raw)
  To: andrew+netdev, davem, edumazet, kuba, pabeni, horms
  Cc: linux-kernel, netdev, han.junyang, ran.ming, han.chengfei,
	zhang.yanze

From: Junyang Han <han.junyang@zte.com.cn>

This series adds initial support for the ZTE DingHai Ethernet controller,
a high-performance PCIe Ethernet device supporting SR-IOV, hardware
offloading, and advanced virtualization features.

Changes from v8:
- Unify the three prefix variants (dh_core* / dh_pf* / zxdh_pf*)
  on the vendor namespace zxdh_* .
- Address the _dev / _device suffix inconsistency: zxdh_pf_device
  -> zxdh_pf_dev, matching zxdh_core_dev and the upstream
  pci_dev / net_dev / usb_dev convention.
- Keep struct zxdh_core_dev and struct zxdh_pf_dev split, with
  zxdh_pf_dev hung off zxdh_core_dev::priv via the container-of
  pattern. The same zxdh_core_dev handle will be shared by the
  SF (Sub-Function) driver queued for the next merge window,
  and by MPF drivers further out. Each device type hangs its own
  private struct off priv: PF -> zxdh_pf_dev, SF -> zxdh_sf_dev (planned).
  Merging now would force a re-split when SF lands.
- Address v8 review comment "introduce functions yet you never use
  them".
- Wire up zxdh_pf_modern_cfg_init() into the probe path and
  zxdh_pf_modern_cfg_uninit() into the remove path.
- Drop the ZXDH_CONFIG_S_* and ZXDH_MSI_NO_VECTOR macros from
  dh_queue.h.

Changes from v7:
- Remove the redundant depends on NET_VENDOR_ZTE
  since it's already implied by the enclosing if NET_VENDOR_ZTE 
  block in the parent Kconfig.
- Check BAR 0 is an IORESOURCE_MEM resource before ioremap().
- Rework .shutdown to quiesce hardware only. 
- Add NULL guards in zxdh_set_mac() and zxdh_get_mac().
- Zero the output buffer in zxdh_pf_get_vf_mac() up front.

- Two further AI-review points were investigated and require no
  code change:

  (1) *len is written before the bounds check inside
      zxdh_pf_map_capability. The two fields written through
      *len and *bar_off are pf_dev->notify_len and
      pf_dev->dev_cfg_bar_off, both populated from this
      function. pf_dev->notify_len is only consulted when
      pf_dev->notify_base != NULL, which only happens when
      the corresponding map_capability() call returned a
      non-NULL pointer; pf_dev->dev_cfg_bar_off is only
      consumed by zxdh_pf_get_vf_mac and
      zxdh_pf_set_vf_mac_reg, both of which gate their
      access on pf_dev->pf_sriov_cap_base. So the early
      *len / *bar_off writes cannot produce values that
      end up consumed.

  (2) The expression off * notify_offset_multiplier is
      u16 * u32; the overflow only triggers when
      notify_offset_multiplier exceeds UINT_MAX / 65535
      (≈ 65537), which is well beyond any value a PCI
      config register would carry for this field. A
      malformed multiplier of that magnitude would also
      fail sane probing of the rest of the cap layout, so
      it cannot reach this arithmetic. The boundaries in
      practice are enforced by the 64-bit comparison at
      the top of zxdh_pf_map_vq_notify's notify_base
      branch, where off is promoted to u64 before the
      multiply. The three subsequent 32-bit multiplies in
      that function only feed pointer arithmetic against
      pf_dev->notify_base; a wrong offset would resolve
      to an address outside the mapped region and fail on
      access rather than corrupt kernel state.


Changes from v6:
- Clean up PCI device ID table (drop `, 0`, use `{ }` sentinel)
- Move dh_core_alloc_priv/dh_core_free_priv from header to .c,
  they don't need to be `static inline`
- Use `if (!x)` and drop the "%i, x" dev_err() messages in
  common/notify cfg init.

Changes from v5:
- Drop dev_info() log spam.
- Propagate the real error code from dh_pf_pci_init() in
  dh_pf_probe() instead of hard-coding -ENOMEM.
- Register devlink only after dh_pf_pci_init() succeeds, and
  in dh_pf_remove()/dh_pf_shutdown() unregister devlink
  before tearing down PCI/mutex/priv.
- Drop the "dh_dev->priv = NULL" assignment from
  dh_core_free_priv().

Changes from v4:
- Fix sparse warning: add __iomem annotation to priv pointer
- Fix Clang format warning
- Use "dinghai:" as patch subject prefix
- Ensure proper patch threading

Note: Sent manually due to temporary git send-email unavailability
in our environment. Will use git send-email or b4 for future
submissions. Apologies for any inconvenience.

Changes from v3:
- Merged patches 1 and 2: 
  Combined initial framework with logging infrastructure
  for better code organization and reduced patch count. This was done because
  the logging infrastructure now uses Linux's built-in dev_err(), dev_info(),
  dev_warn(), etc. macros instead of a custom logging system.
- Removed unnecessary variable initialization: 
  Fixed "don't initialise variables".
- Fixed variable declaration order: 
  Applied "Reverse Christmas tree" ordering with variables
  declared from longest to shortest line length.
- Code quality improvements:
  Fixed all checkpatch.pl issues (alignment, formatting, etc.).

Changes from v2:
- Address maintainer feedback from v2 review:
  * Remove meaningless initialization
  * Change dh_pf_pci_table to static const for better encapsulation
  * Simplify MODULE_DESCRIPTION for brevity
- Coding style improvements:
  * Ensure all lines are within 80-column limit
  * Use kernel types (u32/u8) consistently throughout
  * Improve code readability with better formatting


Changes from v1 (addressing feedback from AndrewLunn):
- Update copyright years to 2022-2026
- Remove DRV_VERSION, MODULE_VERSION and related boilerplate
- Fix MODULE_AUTHOR to use person with email address
- Use module_pci_driver() instead of manual init/exit
- Remove empty suspend/resume callbacks
- Replace char priv[] flexible array with void *priv + kzalloc
- Switch logging from printk wrappers to dev_*() based macros
- Remove dh_helper.h and dh_log.c, simplify to dh_log.h only
- Fix variable declaration ordering (reverse Christmas tree)
- Remove unnecessary NULL check in remove and pf_dev=NULL in probe
- Fix indentation and remove unnecessary type casts
- Use kernel idiomatic "if (ret)" style

This is the initial submission and only includes the PF (Physical Function)
driver. The VF (Virtual Function) driver will be submitted separately.

Junyang Han (2):
  dinghai: add ZTE network driver support
  dinghai: add hardware register access and PCI capability scanning

 MAINTAINERS                                 |   6 +
 drivers/net/ethernet/Kconfig                |   1 +
 drivers/net/ethernet/Makefile               |   1 +
 drivers/net/ethernet/zte/Kconfig            |  20 +
 drivers/net/ethernet/zte/Makefile           |   6 +
 drivers/net/ethernet/zte/dinghai/Kconfig    |  34 ++
 drivers/net/ethernet/zte/dinghai/Makefile   |   9 +
 drivers/net/ethernet/zte/dinghai/dh_queue.h |  54 +++
 drivers/net/ethernet/zte/dinghai/en_pf.c    | 465 ++++++++++++++++++++
 drivers/net/ethernet/zte/dinghai/en_pf.h    | 101 +++++
 10 files changed, 697 insertions(+)
 create mode 100644 drivers/net/ethernet/zte/Kconfig
 create mode 100644 drivers/net/ethernet/zte/Makefile
 create mode 100644 drivers/net/ethernet/zte/dinghai/Kconfig
 create mode 100644 drivers/net/ethernet/zte/dinghai/Makefile
 create mode 100644 drivers/net/ethernet/zte/dinghai/dh_queue.h
 create mode 100644 drivers/net/ethernet/zte/dinghai/en_pf.c
 create mode 100644 drivers/net/ethernet/zte/dinghai/en_pf.h

-- 
2.27.0

^ permalink raw reply	[flat|nested] 3+ messages in thread

* [PATCH net-next v9 1/2] dinghai: add ZTE network driver support
  2026-07-20 10:06 [PATCH net-next v9 0/2] Add ZTE DingHai Ethernet PF driver han.junyang
@ 2026-07-20 10:10 ` han.junyang
  2026-07-20 10:14 ` [PATCH net-next v9 2/2] dinghai: add hardware register access and PCI capability scanning han.junyang
  1 sibling, 0 replies; 3+ messages in thread
From: han.junyang @ 2026-07-20 10:10 UTC (permalink / raw)
  To: andrew+netdev, davem, edumazet, kuba, pabeni, horms
  Cc: linux-kernel, netdev, han.junyang, ran.ming, han.chengfei,
	zhang.yanze

From: Junyang Han <han.junyang@zte.com.cn>

Add basic framework for ZTE DingHai ethernet PF driver, including
Kconfig/Makefile build support and PCIe device probe/remove skeleton.

Signed-off-by: Junyang Han <han.junyang@zte.com.cn>
---
 MAINTAINERS                               |   6 +
 drivers/net/ethernet/Kconfig              |   1 +
 drivers/net/ethernet/Makefile             |   1 +
 drivers/net/ethernet/zte/Kconfig          |  20 +++
 drivers/net/ethernet/zte/Makefile         |   6 +
 drivers/net/ethernet/zte/dinghai/Kconfig  |  34 ++++
 drivers/net/ethernet/zte/dinghai/Makefile |   9 +
 drivers/net/ethernet/zte/dinghai/en_pf.c  | 195 ++++++++++++++++++++++
 drivers/net/ethernet/zte/dinghai/en_pf.h  |  52 ++++++
 9 files changed, 324 insertions(+)
 create mode 100644 drivers/net/ethernet/zte/Kconfig
 create mode 100644 drivers/net/ethernet/zte/Makefile
 create mode 100644 drivers/net/ethernet/zte/dinghai/Kconfig
 create mode 100644 drivers/net/ethernet/zte/dinghai/Makefile
 create mode 100644 drivers/net/ethernet/zte/dinghai/en_pf.c
 create mode 100644 drivers/net/ethernet/zte/dinghai/en_pf.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 2fb1c75afd16..73692b09bf7b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -29440,6 +29440,12 @@ S:	Maintained
 T:	git git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git
 F:	sound/hda/codecs/senarytech.c

+ZTE DINGHAI ETHERNET DRIVER
+M:	Junyang Han <han.junyang@zte.com.cn>
+L:	netdev@vger.kernel.org
+S:	Maintained
+F:	drivers/net/ethernet/zte/
+
 THE REST
 M:	Linus Torvalds <torvalds@linux-foundation.org>
 L:	linux-kernel@vger.kernel.org
diff --git a/drivers/net/ethernet/Kconfig b/drivers/net/ethernet/Kconfig
index b8f70e2a1763..c2b6996b0cfe 100644
--- a/drivers/net/ethernet/Kconfig
+++ b/drivers/net/ethernet/Kconfig
@@ -188,5 +188,6 @@ source "drivers/net/ethernet/wangxun/Kconfig"
 source "drivers/net/ethernet/wiznet/Kconfig"
 source "drivers/net/ethernet/xilinx/Kconfig"
 source "drivers/net/ethernet/xircom/Kconfig"
+source "drivers/net/ethernet/zte/Kconfig"

 endif # ETHERNET
diff --git a/drivers/net/ethernet/Makefile b/drivers/net/ethernet/Makefile
index 57344fec6ce0..a34bcbd4df4e 100644
--- a/drivers/net/ethernet/Makefile
+++ b/drivers/net/ethernet/Makefile
@@ -104,3 +104,4 @@ obj-$(CONFIG_NET_VENDOR_XIRCOM) += xircom/
 obj-$(CONFIG_NET_VENDOR_SYNOPSYS) += synopsys/
 obj-$(CONFIG_NET_VENDOR_PENSANDO) += pensando/
 obj-$(CONFIG_OA_TC6) += oa_tc6.o
+obj-$(CONFIG_NET_VENDOR_ZTE) += zte/
diff --git a/drivers/net/ethernet/zte/Kconfig b/drivers/net/ethernet/zte/Kconfig
new file mode 100644
index 000000000000..b95c2fc7db77
--- /dev/null
+++ b/drivers/net/ethernet/zte/Kconfig
@@ -0,0 +1,20 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# ZTE driver configuration
+#
+
+config NET_VENDOR_ZTE
+    bool "ZTE devices"
+    default y
+    help
+      If you have a network (Ethernet) card belonging to this class, say Y.
+      Note that the answer to this question doesn't directly affect the
+      kernel: saying N will just cause the configurator to skip all
+      the questions about Zte cards. If you say Y, you will be asked
+      for your specific card in the following questions.
+
+if NET_VENDOR_ZTE
+
+source "drivers/net/ethernet/zte/dinghai/Kconfig"
+
+endif # NET_VENDOR_ZTE
diff --git a/drivers/net/ethernet/zte/Makefile b/drivers/net/ethernet/zte/Makefile
new file mode 100644
index 000000000000..cd9929b61559
--- /dev/null
+++ b/drivers/net/ethernet/zte/Makefile
@@ -0,0 +1,6 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Makefile for the ZTE device drivers
+#
+
+obj-$(CONFIG_DINGHAI) += dinghai/
diff --git a/drivers/net/ethernet/zte/dinghai/Kconfig b/drivers/net/ethernet/zte/dinghai/Kconfig
new file mode 100644
index 000000000000..121be3bf7707
--- /dev/null
+++ b/drivers/net/ethernet/zte/dinghai/Kconfig
@@ -0,0 +1,34 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# ZTE DingHai Ethernet driver configuration
+#
+
+config DINGHAI
+    bool "ZTE DingHai Ethernet driver"
+    depends on PCI
+    select NET_DEVLINK
+    help
+      This driver supports ZTE DingHai Ethernet devices.
+
+      DingHai is a high-performance Ethernet controller that supports
+      multiple features including hardware offloading, SR-IOV, and
+      advanced virtualization capabilities.
+
+      If you say Y here, you can select specific driver variants below.
+
+      If unsure, say N.
+
+if DINGHAI
+
+config DINGHAI_PF
+    tristate "ZTE DingHai PF (Physical Function) driver"
+    help
+      This driver supports ZTE DingHai PCI Express Ethernet
+      adapters (PF).
+
+      To compile this driver as a module, choose M here. The module
+      will be named dinghai10e.
+
+      If unsure, say N.
+
+endif # DINGHAI
diff --git a/drivers/net/ethernet/zte/dinghai/Makefile b/drivers/net/ethernet/zte/dinghai/Makefile
new file mode 100644
index 000000000000..10df4eb79e80
--- /dev/null
+++ b/drivers/net/ethernet/zte/dinghai/Makefile
@@ -0,0 +1,9 @@
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Makefile for ZTE DingHai Ethernet driver
+#
+
+ccflags-y += -I$(src)
+
+obj-$(CONFIG_DINGHAI_PF) += dinghai10e.o
+dinghai10e-y := en_pf.o
diff --git a/drivers/net/ethernet/zte/dinghai/en_pf.c b/drivers/net/ethernet/zte/dinghai/en_pf.c
new file mode 100644
index 000000000000..27ab35d69920
--- /dev/null
+++ b/drivers/net/ethernet/zte/dinghai/en_pf.c
@@ -0,0 +1,195 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * ZTE DingHai Ethernet driver
+ * Copyright (c) 2022-2026, ZTE Corporation.
+ */
+
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <net/devlink.h>
+#include <linux/dma-mapping.h>
+#include "en_pf.h"
+
+MODULE_AUTHOR("Junyang Han <han.junyang@zte.com.cn>");
+MODULE_DESCRIPTION("ZTE DingHai series Ethernet driver");
+MODULE_LICENSE("GPL");
+
+static const struct devlink_ops zxdh_pf_devlink_ops = {};
+
+static const struct pci_device_id zxdh_pf_pci_table[] = {
+	{ PCI_DEVICE(ZXDH_PF_VENDOR_ID, ZXDH_PF_DEVICE_ID) },
+	{ PCI_DEVICE(ZXDH_PF_VENDOR_ID, ZXDH_VF_DEVICE_ID) },
+	{ }
+};
+
+MODULE_DEVICE_TABLE(pci, zxdh_pf_pci_table);
+
+void *zxdh_core_alloc_priv(struct zxdh_core_dev *zxdh_dev, size_t size)
+{
+	void *priv = kzalloc(size, GFP_KERNEL);
+
+	if (priv)
+		zxdh_dev->priv = priv;
+	return priv;
+}
+
+void zxdh_core_free_priv(struct zxdh_core_dev *zxdh_dev)
+{
+	kfree(zxdh_dev->priv);
+}
+
+static int zxdh_pf_pci_init(struct zxdh_core_dev *zxdh_dev)
+{
+	struct zxdh_pf_dev *pf_dev = zxdh_dev->priv;
+	int ret;
+
+	pci_set_drvdata(zxdh_dev->pdev, zxdh_dev);
+
+	ret = pci_enable_device(zxdh_dev->pdev);
+	if (ret) {
+		dev_err(zxdh_dev->device, "pci_enable_device failed: %d\n", ret);
+		return ret;
+	}
+
+	ret = dma_set_mask_and_coherent(zxdh_dev->device, DMA_BIT_MASK(64));
+	if (ret) {
+		ret = dma_set_mask_and_coherent(zxdh_dev->device, DMA_BIT_MASK(32));
+		if (ret) {
+			dev_err(zxdh_dev->device, "dma_set_mask_and_coherent failed: %d\n", ret);
+			goto err_pci;
+		}
+	}
+
+	ret = pci_request_selected_regions(zxdh_dev->pdev,
+					   pci_select_bars(zxdh_dev->pdev, IORESOURCE_MEM),
+					   "dh-pf");
+	if (ret) {
+		dev_err(zxdh_dev->device, "pci_request_selected_regions failed: %d\n", ret);
+		goto err_pci;
+	}
+
+	pci_set_master(zxdh_dev->pdev);
+	ret = pci_save_state(zxdh_dev->pdev);
+	if (ret) {
+		dev_err(zxdh_dev->device, "pci_save_state failed: %d\n", ret);
+		goto err_pci_save_state;
+	}
+
+	if (!(pci_resource_flags(zxdh_dev->pdev, 0) & IORESOURCE_MEM)) {
+		ret = -ENODEV;
+		dev_err(zxdh_dev->device, "BAR 0 is not an MMIO resource\n");
+		goto err_pci_save_state;
+	}
+
+	pf_dev->pci_ioremap_addr[0] =
+		ioremap(pci_resource_start(zxdh_dev->pdev, 0),
+			pci_resource_len(zxdh_dev->pdev, 0));
+	if (!pf_dev->pci_ioremap_addr[0]) {
+		ret = -ENOMEM;
+		dev_err(zxdh_dev->device, "dh pf pci ioremap failed\n");
+		goto err_pci_save_state;
+	}
+
+	return 0;
+
+err_pci_save_state:
+	pci_release_selected_regions(zxdh_dev->pdev,
+				     pci_select_bars(zxdh_dev->pdev, IORESOURCE_MEM));
+err_pci:
+	pci_disable_device(zxdh_dev->pdev);
+	return ret;
+}
+
+void zxdh_pf_pci_close(struct zxdh_core_dev *zxdh_dev)
+{
+	struct zxdh_pf_dev *pf_dev = zxdh_dev->priv;
+
+	iounmap(pf_dev->pci_ioremap_addr[0]);
+	pci_release_selected_regions(zxdh_dev->pdev,
+				     pci_select_bars(zxdh_dev->pdev, IORESOURCE_MEM));
+	pci_disable_device(zxdh_dev->pdev);
+}
+
+static int zxdh_pf_probe(struct pci_dev *pdev, const struct pci_device_id *id)
+{
+	struct zxdh_pf_dev *pf_dev;
+	struct zxdh_core_dev *zxdh_dev;
+	struct devlink *devlink;
+	int ret;
+
+	devlink = devlink_alloc(&zxdh_pf_devlink_ops, sizeof(struct zxdh_core_dev),
+				&pdev->dev);
+	if (!devlink) {
+		dev_err(&pdev->dev, "zxdh_pf devlink alloc failed\n");
+		return -ENOMEM;
+	}
+
+	zxdh_dev = devlink_priv(devlink);
+	zxdh_dev->device = &pdev->dev;
+	zxdh_dev->pdev = pdev;
+	zxdh_dev->devlink = devlink;
+
+	pf_dev = zxdh_core_alloc_priv(zxdh_dev, sizeof(*pf_dev));
+	if (!pf_dev) {
+		dev_err(&pdev->dev, "zxdh_pf_dev alloc failed\n");
+		ret = -ENOMEM;
+		goto err_pf_dev;
+	}
+
+	pf_dev->bar_chan_valid = false;
+	pf_dev->vepa = false;
+	mutex_init(&zxdh_dev->lock);
+	mutex_init(&pf_dev->irq_lock);
+
+	zxdh_dev->coredev_type = GET_COREDEV_TYPE(pdev);
+
+	ret = zxdh_pf_pci_init(zxdh_dev);
+	if (ret) {
+		dev_err(&pdev->dev, "zxdh_pf_pci_init failed: %d\n", ret);
+		goto err_cfg_init;
+	}
+
+	devlink_register(devlink);
+
+	return 0;
+
+err_cfg_init:
+	mutex_destroy(&pf_dev->irq_lock);
+	mutex_destroy(&zxdh_dev->lock);
+	zxdh_core_free_priv(zxdh_dev);
+err_pf_dev:
+	devlink_free(devlink);
+	return ret;
+}
+
+static void zxdh_pf_remove(struct pci_dev *pdev)
+{
+	struct zxdh_core_dev *zxdh_dev = pci_get_drvdata(pdev);
+	struct devlink *devlink = priv_to_devlink(zxdh_dev);
+	struct zxdh_pf_dev *pf_dev = zxdh_dev->priv;
+
+	devlink_unregister(devlink);
+	zxdh_pf_pci_close(zxdh_dev);
+	mutex_destroy(&pf_dev->irq_lock);
+	mutex_destroy(&zxdh_dev->lock);
+	zxdh_core_free_priv(zxdh_dev);
+	devlink_free(devlink);
+	pci_set_drvdata(pdev, NULL);
+}
+
+static void zxdh_pf_shutdown(struct pci_dev *pdev)
+{
+	if (system_state == SYSTEM_POWER_OFF)
+		pci_set_power_state(pdev, PCI_D3hot);
+	pci_disable_device(pdev);
+}
+
+static struct pci_driver zxdh_pf_driver = {
+	.name = "dinghai10e",
+	.id_table = zxdh_pf_pci_table,
+	.probe = zxdh_pf_probe,
+	.remove = zxdh_pf_remove,
+	.shutdown = zxdh_pf_shutdown,
+};
+
+module_pci_driver(zxdh_pf_driver);
diff --git a/drivers/net/ethernet/zte/dinghai/en_pf.h b/drivers/net/ethernet/zte/dinghai/en_pf.h
new file mode 100644
index 000000000000..65eac936505b
--- /dev/null
+++ b/drivers/net/ethernet/zte/dinghai/en_pf.h
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * ZTE DingHai Ethernet driver - PF header
+ * Copyright (c) 2022-2026, ZTE Corporation.
+ */
+
+#ifndef __ZXDH_EN_PF_H__
+#define __ZXDH_EN_PF_H__
+
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/mutex.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+
+#define ZXDH_PF_VENDOR_ID	0x1cf2
+#define ZXDH_PF_DEVICE_ID	0x8040
+#define ZXDH_VF_DEVICE_ID	0x8041
+
+enum zxdh_coredev_type {
+	DH_COREDEV_PF,
+	DH_COREDEV_VF,
+	DH_COREDEV_SF,
+	DH_COREDEV_MPF
+};
+
+struct devlink;
+
+struct zxdh_core_dev {
+	struct device *device;
+	enum zxdh_coredev_type coredev_type;
+	struct pci_dev *pdev;
+	struct devlink *devlink;
+	struct mutex lock; /* Protects device configuration */
+	void *priv;
+};
+
+struct zxdh_pf_dev {
+	void __iomem *pci_ioremap_addr[6];
+	bool bar_chan_valid;
+	bool vepa;
+	struct mutex irq_lock; /* Protects IRQ operations */
+};
+
+#define GET_COREDEV_TYPE(pdev) \
+	((pdev)->device == ZXDH_VF_DEVICE_ID ? DH_COREDEV_VF : DH_COREDEV_PF)
+
+void *zxdh_core_alloc_priv(struct zxdh_core_dev *zxdh_dev, size_t size);
+void zxdh_core_free_priv(struct zxdh_core_dev *zxdh_dev);
+void zxdh_pf_pci_close(struct zxdh_core_dev *zxdh_dev);
+
+#endif /* __ZXDH_EN_PF_H__ */
-- 
2.27.0

^ permalink raw reply related	[flat|nested] 3+ messages in thread

* [PATCH net-next v9 2/2] dinghai: add hardware register access and PCI  capability scanning
  2026-07-20 10:06 [PATCH net-next v9 0/2] Add ZTE DingHai Ethernet PF driver han.junyang
  2026-07-20 10:10 ` [PATCH net-next v9 1/2] dinghai: add ZTE network driver support han.junyang
@ 2026-07-20 10:14 ` han.junyang
  1 sibling, 0 replies; 3+ messages in thread
From: han.junyang @ 2026-07-20 10:14 UTC (permalink / raw)
  To: andrew+netdev, davem, edumazet, kuba, pabeni, horms
  Cc: linux-kernel, netdev, han.junyang, ran.ming, han.chengfei,
	zhang.yanze

From: Junyang Han <han.junyang@zte.com.cn>

Implement PCI configuration space access, BAR mapping, capability
scanning (common/notify/device), and hardware queue register
definitions for DingHai PF device.

Signed-off-by: Junyang Han <han.junyang@zte.com.cn>
---
 drivers/net/ethernet/zte/dinghai/dh_queue.h |  54 ++++
 drivers/net/ethernet/zte/dinghai/en_pf.c    | 270 ++++++++++++++++++++
 drivers/net/ethernet/zte/dinghai/en_pf.h    |  49 ++++
 3 files changed, 373 insertions(+)
 create mode 100644 drivers/net/ethernet/zte/dinghai/dh_queue.h

diff --git a/drivers/net/ethernet/zte/dinghai/dh_queue.h b/drivers/net/ethernet/zte/dinghai/dh_queue.h
new file mode 100644
index 000000000000..afac67f7147c
--- /dev/null
+++ b/drivers/net/ethernet/zte/dinghai/dh_queue.h
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * ZTE DingHai Ethernet driver - PCI capability definitions
+ * Copyright (c) 2022-2026, ZTE Corporation.
+ */
+
+#ifndef __DH_QUEUE_H__
+#define __DH_QUEUE_H__
+
+/* This is the PCI capability header: */
+struct zxdh_pf_pci_cap {
+	__u8 cap_vndr;		/* Generic PCI field: PCI_CAP_ID_VNDR */
+	__u8 cap_next;		/* Generic PCI field: next ptr. */
+	__u8 cap_len;		/* Generic PCI field: capability length */
+	__u8 cfg_type;		/* Identifies the structure. */
+	__u8 bar;		/* Where to find it. */
+	__u8 id;		/* Multiple capabilities of the same type */
+	__u8 padding[2];		/* Pad to full dword. */
+	__le32 offset;		/* Offset within bar. */
+	__le32 length;		/* Length of the structure, in bytes. */
+};
+
+/* Fields in ZXDH_PF_PCI_CAP_COMMON_CFG: */
+struct zxdh_pf_pci_common_cfg {
+	/* About the whole device. */
+	__le32 device_feature_select; /* read-write */
+	__le32 device_feature;	/* read-only */
+	__le32 guest_feature_select; /* read-write */
+	__le32 guest_feature;		/* read-write */
+	__le16 msix_config;		/* read-write */
+	__le16 num_queues;		/* read-only */
+	__u8 device_status;		/* read-write */
+	__u8 config_generation;	/* read-only */
+
+	/* About a specific virtqueue. */
+	__le16 queue_select;		/* read-write */
+	__le16 queue_size;		/* read-write, power of 2. */
+	__le16 queue_msix_vector;	/* read-write */
+	__le16 queue_enable;		/* read-write */
+	__le16 queue_notify_off;	/* read-only */
+	__le32 queue_desc_lo;		/* read-write */
+	__le32 queue_desc_hi;		/* read-write */
+	__le32 queue_avail_lo;		/* read-write */
+	__le32 queue_avail_hi;		/* read-write */
+	__le32 queue_used_lo;		/* read-write */
+	__le32 queue_used_hi;		/* read-write */
+};
+
+struct zxdh_pf_pci_notify_cap {
+	struct zxdh_pf_pci_cap cap;
+	__le32 notify_off_multiplier; /* Multiplier for queue_notify_off. */
+};
+
+#endif /* __DH_QUEUE_H__ */
diff --git a/drivers/net/ethernet/zte/dinghai/en_pf.c b/drivers/net/ethernet/zte/dinghai/en_pf.c
index 27ab35d69920..ae5a77ddaca5 100644
--- a/drivers/net/ethernet/zte/dinghai/en_pf.c
+++ b/drivers/net/ethernet/zte/dinghai/en_pf.c
@@ -8,7 +8,9 @@
 #include <linux/pci.h>
 #include <net/devlink.h>
 #include <linux/dma-mapping.h>
+#include <linux/etherdevice.h>
 #include "en_pf.h"
+#include "dh_queue.h"

 MODULE_AUTHOR("Junyang Han <han.junyang@zte.com.cn>");
 MODULE_DESCRIPTION("ZTE DingHai series Ethernet driver");
@@ -110,6 +112,265 @@ void zxdh_pf_pci_close(struct zxdh_core_dev *zxdh_dev)
 	pci_disable_device(zxdh_dev->pdev);
 }

+int zxdh_pf_pci_find_capability(struct pci_dev *pdev, u8 cfg_type,
+				u32 ioresource_types, int *bars)
+{
+	int pos;
+	u8 type;
+	u8 bar;
+
+	for (pos = pci_find_capability(pdev, PCI_CAP_ID_VNDR); pos > 0;
+	     pos = pci_find_next_capability(pdev, pos, PCI_CAP_ID_VNDR)) {
+		pci_read_config_byte(pdev,
+				     pos + offsetof(struct zxdh_pf_pci_cap,
+							cfg_type), &type);
+		pci_read_config_byte(pdev,
+				     pos + offsetof(struct zxdh_pf_pci_cap, bar), &bar);
+
+		/* ignore structures with reserved BAR values */
+		if (bar > ZXDH_PF_MAX_BAR_VAL)
+			continue;
+
+		if (type == cfg_type) {
+			if (pci_resource_len(pdev, bar) &&
+			    pci_resource_flags(pdev, bar) & ioresource_types) {
+				*bars |= (1 << bar);
+				return pos;
+			}
+		}
+	}
+
+	return 0;
+}
+
+void __iomem *zxdh_pf_map_capability(struct zxdh_core_dev *zxdh_dev, int off,
+				     size_t minlen, u32 align,
+				     u32 start, u32 size,
+				     size_t *len, resource_size_t *pa,
+				     u32 *bar_off)
+{
+	struct pci_dev *pdev = zxdh_dev->pdev;
+	void __iomem *p;
+	u32 offset;
+	u32 length;
+	u8 bar;
+
+	pci_read_config_byte(pdev,
+			     off + offsetof(struct zxdh_pf_pci_cap, bar), &bar);
+	pci_read_config_dword(pdev,
+			      off + offsetof(struct zxdh_pf_pci_cap,
+						offset), &offset);
+	pci_read_config_dword(pdev,
+			      off + offsetof(struct zxdh_pf_pci_cap,
+						length), &length);
+
+	if (bar_off)
+		*bar_off = offset;
+
+	if (length <= start) {
+		dev_err(zxdh_dev->device, "bad capability len %u (>%u expected)\n",
+			length, start);
+		return NULL;
+	}
+
+	if (length - start < minlen) {
+		dev_err(zxdh_dev->device, "bad capability len %u (>=%zu expected)\n",
+			length, minlen);
+		return NULL;
+	}
+
+	length -= start;
+	if (start + offset < offset) {
+		dev_err(zxdh_dev->device, "map wrap-around %u+%u\n", start, offset);
+		return NULL;
+	}
+
+	offset += start;
+	if (offset & (align - 1)) {
+		dev_err(zxdh_dev->device, "offset %u not aligned to %u\n", offset, align);
+		return NULL;
+	}
+
+	if (length > size)
+		length = size;
+
+	if (len)
+		*len = length;
+
+	if (minlen + offset < minlen ||
+	    minlen + offset > pci_resource_len(pdev, bar)) {
+		dev_err(zxdh_dev->device,
+			"map custom queue %zu@%u out of range on bar %u length %lu\n",
+			minlen, offset, bar,
+			(unsigned long)pci_resource_len(pdev, bar));
+		return NULL;
+	}
+
+	p = pci_iomap_range(pdev, bar, offset, length);
+	if (!p) {
+		dev_err(zxdh_dev->device, "unable to map custom queue %u@%u on bar %u\n",
+			length, offset, bar);
+	} else if (pa) {
+		*pa = pci_resource_start(pdev, bar) + offset;
+	}
+
+	return p;
+}
+
+int zxdh_pf_common_cfg_init(struct zxdh_core_dev *zxdh_dev)
+{
+	struct zxdh_pf_dev *pf_dev = zxdh_dev->priv;
+	struct pci_dev *pdev = zxdh_dev->pdev;
+	int common;
+
+	/* check for a common config: if not, use legacy mode (bar 0). */
+	common = zxdh_pf_pci_find_capability(pdev, ZXDH_PCI_CAP_COMMON_CFG,
+					     IORESOURCE_IO | IORESOURCE_MEM,
+					     &pf_dev->modern_bars);
+	if (!common) {
+		dev_err(zxdh_dev->device,
+			"missing capabilities, leaving for legacy driver\n");
+		return -ENODEV;
+	}
+
+	pf_dev->common = zxdh_pf_map_capability(zxdh_dev, common,
+						sizeof(struct zxdh_pf_pci_common_cfg),
+						ZXDH_PF_ALIGN4, 0,
+						sizeof(struct zxdh_pf_pci_common_cfg),
+						NULL, NULL, NULL);
+	if (!pf_dev->common) {
+		dev_err(zxdh_dev->device, "pf_dev->common is null\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+int zxdh_pf_notify_cfg_init(struct zxdh_core_dev *zxdh_dev)
+{
+	struct zxdh_pf_dev *pf_dev = zxdh_dev->priv;
+	struct pci_dev *pdev = zxdh_dev->pdev;
+	u32 notify_length;
+	u32 notify_offset;
+	int notify;
+
+	/* If common is there, these should be too... */
+	notify = zxdh_pf_pci_find_capability(pdev, ZXDH_PCI_CAP_NOTIFY_CFG,
+					     IORESOURCE_IO | IORESOURCE_MEM,
+					     &pf_dev->modern_bars);
+	if (!notify) {
+		dev_err(zxdh_dev->device, "missing notify cfg capability\n");
+		return -EINVAL;
+	}
+
+	pci_read_config_dword(pdev,
+			      notify + offsetof(struct zxdh_pf_pci_notify_cap,
+				notify_off_multiplier),
+		&pf_dev->notify_offset_multiplier);
+	pci_read_config_dword(pdev,
+			      notify + offsetof(struct zxdh_pf_pci_notify_cap,
+				cap.length), &notify_length);
+	pci_read_config_dword(pdev,
+			      notify + offsetof(struct zxdh_pf_pci_notify_cap,
+				cap.offset), &notify_offset);
+
+	/* We don't know how many VQs we'll map, ahead of the time.
+	 * If notify length is small, map it all now. Otherwise,
+	 * map each VQ individually later.
+	 */
+	if (notify_length + (notify_offset % PAGE_SIZE) <= PAGE_SIZE) {
+		pf_dev->notify_base = zxdh_pf_map_capability(zxdh_dev, notify,
+							     ZXDH_PF_MAP_MINLEN2,
+							    ZXDH_PF_ALIGN2, 0,
+							    notify_length,
+							    &pf_dev->notify_len,
+							    &pf_dev->notify_pa, NULL);
+		if (!pf_dev->notify_base) {
+			dev_err(zxdh_dev->device, "pf_dev->notify_base is null\n");
+			return -EINVAL;
+		}
+	} else {
+		pf_dev->notify_map_cap = notify;
+	}
+
+	return 0;
+}
+
+int zxdh_pf_device_cfg_init(struct zxdh_core_dev *zxdh_dev)
+{
+	struct zxdh_pf_dev *pf_dev = zxdh_dev->priv;
+	struct pci_dev *pdev = zxdh_dev->pdev;
+	int device;
+
+	/* Device capability is only mandatory for
+	 * devices that have device-specific configuration.
+	 */
+	device = zxdh_pf_pci_find_capability(pdev, ZXDH_PCI_CAP_DEVICE_CFG,
+					     IORESOURCE_IO | IORESOURCE_MEM,
+					     &pf_dev->modern_bars);
+
+	/* we don't know how much we should map,
+	 * but PAGE_SIZE is more than enough for all existing devices.
+	 */
+	if (device) {
+		pf_dev->device = zxdh_pf_map_capability(zxdh_dev, device, 0,
+							ZXDH_PF_ALIGN4, 0, PAGE_SIZE,
+						       &pf_dev->device_len, NULL,
+						       &pf_dev->dev_cfg_bar_off);
+		if (!pf_dev->device) {
+			dev_err(zxdh_dev->device, "pf_dev->device is null\n");
+			return -EINVAL;
+		}
+	}
+	return 0;
+}
+
+void zxdh_pf_modern_cfg_uninit(struct zxdh_core_dev *zxdh_dev)
+{
+	struct zxdh_pf_dev *pf_dev = zxdh_dev->priv;
+	struct pci_dev *pdev = zxdh_dev->pdev;
+
+	if (pf_dev->device)
+		pci_iounmap(pdev, pf_dev->device);
+	if (pf_dev->notify_base)
+		pci_iounmap(pdev, pf_dev->notify_base);
+	pci_iounmap(pdev, pf_dev->common);
+}
+
+int zxdh_pf_modern_cfg_init(struct zxdh_core_dev *zxdh_dev)
+{
+	struct zxdh_pf_dev *pf_dev = zxdh_dev->priv;
+	struct pci_dev *pdev = zxdh_dev->pdev;
+	int ret;
+
+	ret = zxdh_pf_common_cfg_init(zxdh_dev);
+	if (ret) {
+		dev_err(zxdh_dev->device, "zxdh_pf_common_cfg_init failed: %d\n", ret);
+		return ret;
+	}
+
+	ret = zxdh_pf_notify_cfg_init(zxdh_dev);
+	if (ret) {
+		dev_err(zxdh_dev->device, "zxdh_pf_notify_cfg_init failed: %d\n", ret);
+		goto err_map_notify;
+	}
+
+	ret = zxdh_pf_device_cfg_init(zxdh_dev);
+	if (ret) {
+		dev_err(zxdh_dev->device, "zxdh_pf_device_cfg_init failed: %d\n", ret);
+		goto err_map_device;
+	}
+
+	return 0;
+
+err_map_device:
+	if (pf_dev->notify_base)
+		pci_iounmap(pdev, pf_dev->notify_base);
+err_map_notify:
+	pci_iounmap(pdev, pf_dev->common);
+	return ret;
+}
+
 static int zxdh_pf_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 {
 	struct zxdh_pf_dev *pf_dev;
@@ -146,6 +407,12 @@ static int zxdh_pf_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 	ret = zxdh_pf_pci_init(zxdh_dev);
 	if (ret) {
 		dev_err(&pdev->dev, "zxdh_pf_pci_init failed: %d\n", ret);
+		goto err_pf_init;
+	}
+
+	ret = zxdh_pf_modern_cfg_init(zxdh_dev);
+	if (ret) {
+		dev_err(&pdev->dev, "zxdh_pf_modern_cfg_init failed: %d\n", ret);
 		goto err_cfg_init;
 	}

@@ -154,6 +421,8 @@ static int zxdh_pf_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 	return 0;

 err_cfg_init:
+	zxdh_pf_pci_close(zxdh_dev);
+err_pf_init:
 	mutex_destroy(&pf_dev->irq_lock);
 	mutex_destroy(&zxdh_dev->lock);
 	zxdh_core_free_priv(zxdh_dev);
@@ -169,6 +438,7 @@ static void zxdh_pf_remove(struct pci_dev *pdev)
 	struct zxdh_pf_dev *pf_dev = zxdh_dev->priv;

 	devlink_unregister(devlink);
+	zxdh_pf_modern_cfg_uninit(zxdh_dev);
 	zxdh_pf_pci_close(zxdh_dev);
 	mutex_destroy(&pf_dev->irq_lock);
 	mutex_destroy(&zxdh_dev->lock);
diff --git a/drivers/net/ethernet/zte/dinghai/en_pf.h b/drivers/net/ethernet/zte/dinghai/en_pf.h
index 65eac936505b..58c5da9a09ff 100644
--- a/drivers/net/ethernet/zte/dinghai/en_pf.h
+++ b/drivers/net/ethernet/zte/dinghai/en_pf.h
@@ -17,6 +17,24 @@
 #define ZXDH_PF_DEVICE_ID	0x8040
 #define ZXDH_VF_DEVICE_ID	0x8041

+/* Common configuration */
+#define ZXDH_PCI_CAP_COMMON_CFG	1
+/* Notifications */
+#define ZXDH_PCI_CAP_NOTIFY_CFG	2
+/* ISR access */
+#define ZXDH_PCI_CAP_ISR_CFG		3
+/* Device specific configuration */
+#define ZXDH_PCI_CAP_DEVICE_CFG	4
+/* PCI configuration access */
+#define ZXDH_PCI_CAP_PCI_CFG		5
+
+#define ZXDH_PF_MAX_BAR_VAL		0x5
+#define ZXDH_PF_ALIGN4			4
+#define ZXDH_PF_ALIGN2			2
+#define ZXDH_PF_MAP_MINLEN2		2
+
+#define ZXDH_DEV_MAC_HIGH_OFFSET	4
+
 enum zxdh_coredev_type {
 	DH_COREDEV_PF,
 	DH_COREDEV_VF,
@@ -36,7 +54,26 @@ struct zxdh_core_dev {
 };

 struct zxdh_pf_dev {
+	struct zxdh_pf_pci_common_cfg __iomem *common;
+	/* Device-specific data (non-legacy mode)  */
+	/* Base of vq notifications (non-legacy mode). */
+	void __iomem *device;
+	void __iomem *notify_base;
+	void __iomem *pf_sriov_cap_base;
+	/* Physical base of vq notifications */
+	resource_size_t notify_pa;
+	/* So we can sanity-check accesses. */
+	size_t notify_len;
+	size_t device_len;
+	/* Capability for when we need to map notifications per-vq. */
+	s32 notify_map_cap;
+	u32 notify_offset_multiplier;
+	/* Multiply queue_notify_off by this value. (non-legacy mode). */
+	s32 modern_bars;
 	void __iomem *pci_ioremap_addr[6];
+	u64 sriov_bar_size;
+	u32 dev_cfg_bar_off;
+	bool packed_status;
 	bool bar_chan_valid;
 	bool vepa;
 	struct mutex irq_lock; /* Protects IRQ operations */
@@ -48,5 +85,17 @@ struct zxdh_pf_dev {
 void *zxdh_core_alloc_priv(struct zxdh_core_dev *zxdh_dev, size_t size);
 void zxdh_core_free_priv(struct zxdh_core_dev *zxdh_dev);
 void zxdh_pf_pci_close(struct zxdh_core_dev *zxdh_dev);
+int zxdh_pf_pci_find_capability(struct pci_dev *pdev, u8 cfg_type,
+				u32 ioresource_types, int *bars);
+void __iomem *zxdh_pf_map_capability(struct zxdh_core_dev *zxdh_dev, int off,
+				     size_t minlen, u32 align,
+				     u32 start, u32 size,
+				     size_t *len, resource_size_t *pa,
+				     u32 *bar_off);
+int zxdh_pf_common_cfg_init(struct zxdh_core_dev *zxdh_dev);
+int zxdh_pf_notify_cfg_init(struct zxdh_core_dev *zxdh_dev);
+int zxdh_pf_device_cfg_init(struct zxdh_core_dev *zxdh_dev);
+void zxdh_pf_modern_cfg_uninit(struct zxdh_core_dev *zxdh_dev);
+int zxdh_pf_modern_cfg_init(struct zxdh_core_dev *zxdh_dev);

 #endif /* __ZXDH_EN_PF_H__ */
-- 
2.27.0

^ permalink raw reply related	[flat|nested] 3+ messages in thread

end of thread, other threads:[~2026-07-20 10:15 UTC | newest]

Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-07-20 10:06 [PATCH net-next v9 0/2] Add ZTE DingHai Ethernet PF driver han.junyang
2026-07-20 10:10 ` [PATCH net-next v9 1/2] dinghai: add ZTE network driver support han.junyang
2026-07-20 10:14 ` [PATCH net-next v9 2/2] dinghai: add hardware register access and PCI capability scanning han.junyang

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox