Linux Hardware Monitor development
 help / color / mirror / Atom feed
* [PATCH v5 0/8] x86/hygon: Add Family 0x18 DF node enumeration and SMN access
@ 2026-09-10  8:27 Lin Wang
  2026-09-10  8:27 ` [PATCH v5 1/8] x86/hygon: Add Family 0x18 DF node enumeration Lin Wang
                   ` (7 more replies)
  0 siblings, 8 replies; 19+ messages in thread
From: Lin Wang @ 2026-09-10  8:27 UTC (permalink / raw)
  To: Borislav Petkov, Yazen Ghannam, Mario Limonciello
  Cc: Pu Wen, Thomas Gleixner, Ingo Molnar, Dave Hansen, H. Peter Anvin,
	Tony Luck, Clemens Ladisch, Guenter Roeck, x86, linux-edac,
	linux-hwmon, linux-kernel

This revision separates the Hygon node and SMN support from the AMD
northbridge and SMN implementations.

The AMD northbridge cache maps dense node numbers to fixed PCI slots.
Hygon discovers DF F3 devices by PCI ID and obtains node identity from
DF registers. Hygon PCI slots do not encode node identity, CPU NodeIds
are sparse across sockets, and SMN roots are grouped by socket. The AMD
mapping cannot represent this topology.

Sashiko reported that v3 restricted AMD northbridge and SMN
initialization while Hygon callers remained. However, amd64_edac and
ATL obtain their node count and F3/F4 devices from the AMD northbridge
cache. A failed fixed-slot F3 lookup discards the cache, so both stop at
their amd_nb_num() checks. k10temp can bind, but derives an SMN node
from the F3 PCI slot, which does not encode Hygon node identity. These
existing matches do not provide usable Hygon support.

This series therefore stops these drivers from matching Hygon before
restricting AMD northbridge and SMN initialization to AMD CPUs.
Follow-up Hygon support for these drivers will use the interfaces added
here and will be submitted separately. The Hygon MCE decoder is
unchanged.

The node and SMN implementation has been tested on a Family 0x18 Model
0x07 system with two sockets, 12 Core and DDR Dies (CDDs) and four I/O
Dies (IODs).

Changes in v5:

  - Stop amd64_edac, ATL and k10temp from matching Hygon before making
    AMD northbridge and SMN initialization AMD-only. (Sashiko)
  - Remove the model-specific Hygon handoff from amd_smn_init(). (Boris)
  - Keep the Hygon DF PCI IDs private to the node implementation. (Bjorn)
  - Report the discovered DF topology after cache initialization.

Link: https://sashiko.dev/#/patchset/20260709075549.626095-1-wanglin%40open-hieco.net [Sashiko v3 review]
Link: https://lore.kernel.org/all/20260901071700.3255382-1-wanglin@open-hieco.net/ [v4]

Lin Wang (8):
  x86/hygon: Add Family 0x18 DF node enumeration
  x86/hygon: Map CPU NodeIds to DF nodes
  EDAC/amd64: Stop matching Hygon CPUs
  RAS/AMD/ATL: Match AMD CPUs only
  hwmon: (k10temp) Stop matching Hygon devices
  x86/amd_nb: Restrict the northbridge framework to AMD CPUs
  x86/amd_node: Restrict SMN setup to AMD CPUs
  x86/hygon: Add Family 0x18 SMN access

 MAINTAINERS                       |   3 +
 arch/x86/Kconfig                  |   4 +
 arch/x86/include/asm/hygon/node.h | 191 ++++++
 arch/x86/kernel/Makefile          |   1 +
 arch/x86/kernel/amd_nb.c          |   6 +-
 arch/x86/kernel/amd_node.c        |   6 +-
 arch/x86/kernel/hygon_node.c      | 980 ++++++++++++++++++++++++++++++
 drivers/edac/amd64_edac.c         |   4 -
 drivers/hwmon/k10temp.c           |   3 +-
 drivers/ras/amd/atl/core.c        |   9 +-
 10 files changed, 1190 insertions(+), 17 deletions(-)
 create mode 100644 arch/x86/include/asm/hygon/node.h
 create mode 100644 arch/x86/kernel/hygon_node.c


base-commit: 50d05c7c76c96b90462f24debacca971d2e86713
-- 
2.43.0


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

* [PATCH v5 1/8] x86/hygon: Add Family 0x18 DF node enumeration
  2026-09-10  8:27 [PATCH v5 0/8] x86/hygon: Add Family 0x18 DF node enumeration and SMN access Lin Wang
@ 2026-09-10  8:27 ` Lin Wang
  2026-09-10  8:32   ` sashiko-bot
  2026-09-10  8:27 ` [PATCH v5 2/8] x86/hygon: Map CPU NodeIds to DF nodes Lin Wang
                   ` (6 subsequent siblings)
  7 siblings, 1 reply; 19+ messages in thread
From: Lin Wang @ 2026-09-10  8:27 UTC (permalink / raw)
  To: Borislav Petkov, Yazen Ghannam, Mario Limonciello
  Cc: Pu Wen, Thomas Gleixner, Ingo Molnar, Dave Hansen, H. Peter Anvin,
	Tony Luck, Clemens Ladisch, Guenter Roeck, x86, linux-edac,
	linux-hwmon, linux-kernel

The AMD node code locates function 3 for node N at PCI slot 0x18 + N.
Hygon has no equivalent node-index-to-slot mapping: DF F3 devices are
discovered by PCI ID and the PCI BDF is only an access point.

F1x200 supplies the socket ID and SocketPresent mask. The DFID comes
from F1x200 or F5x180, depending on the CPU model. The (socket ID,
DFID) pair identifies a DF node. The cache is published only when the
socket IDs observed during PCI enumeration match SocketPresent.

The resulting cache places Core and DDR Dies (CDDs) before I/O Dies
(IODs), with each group ordered by socket and DFID. The node interfaces
provide node counts and identity, F3/F4 lookup, and PCI device-to-node
lookup.

Signed-off-by: Lin Wang <wanglin@open-hieco.net>
---
 MAINTAINERS                       |   3 +
 arch/x86/Kconfig                  |   4 +
 arch/x86/include/asm/hygon/node.h | 133 ++++++
 arch/x86/kernel/Makefile          |   1 +
 arch/x86/kernel/hygon_node.c      | 685 ++++++++++++++++++++++++++++++
 5 files changed, 826 insertions(+)
 create mode 100644 arch/x86/include/asm/hygon/node.h
 create mode 100644 arch/x86/kernel/hygon_node.c

diff --git a/MAINTAINERS b/MAINTAINERS
index c392ded5322a..43e24c81c0b2 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -12134,9 +12134,12 @@ F:	drivers/input/touchscreen/hycon-hy46xx.c
 
 HYGON PROCESSOR SUPPORT
 M:	Pu Wen <puwen@hygon.cn>
+M:	Lin Wang <wanglin@open-hieco.net>
 L:	linux-kernel@vger.kernel.org
 S:	Maintained
+F:	arch/x86/include/asm/hygon/
 F:	arch/x86/kernel/cpu/hygon.c
+F:	arch/x86/kernel/hygon_node.c
 
 HYNIX HI556 SENSOR DRIVER
 M:	Sakari Ailus <sakari.ailus@linux.intel.com>
diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig
index 15fd9ec5ecac..6d72e62d87f7 100644
--- a/arch/x86/Kconfig
+++ b/arch/x86/Kconfig
@@ -3078,6 +3078,10 @@ config AMD_NODE
 	def_bool y
 	depends on CPU_SUP_AMD && PCI
 
+config HYGON_NODE
+	def_bool y
+	depends on CPU_SUP_HYGON && PCI
+
 endmenu
 
 menu "Binary Emulations"
diff --git a/arch/x86/include/asm/hygon/node.h b/arch/x86/include/asm/hygon/node.h
new file mode 100644
index 000000000000..77e9f5354837
--- /dev/null
+++ b/arch/x86/include/asm/hygon/node.h
@@ -0,0 +1,133 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Hygon Family 0x18 Data Fabric node enumeration API
+ *
+ * This header declares Hygon Fam18h DF node enumeration and function
+ * access interfaces.
+ */
+#ifndef _ASM_X86_HYGON_NODE_H
+#define _ASM_X86_HYGON_NODE_H
+
+#include <linux/errno.h>
+#include <linux/types.h>
+
+struct pci_dev;
+
+/* DF function indices supported by hygon_node_get_func(). */
+#define HYGON_DF_F3	3	/* misc */
+#define HYGON_DF_F4	4	/* link */
+
+/*
+ * Hygon Core and DDR Dies (CDDs) start at DFID 4. Each CDD contains
+ * CPU cores and UMCs. I/O Dies (IODs) occupy DFIDs 0-3.
+ */
+#define HYGON_CDD_DFID_BASE	4
+
+/**
+ * enum hygon_node_type - DF node type, derived from DFID
+ * @HYGON_NODE_IOD: I/O die (DFID < HYGON_CDD_DFID_BASE); no UMC.
+ * @HYGON_NODE_CDD: Core and DDR Die (DFID >= HYGON_CDD_DFID_BASE);
+ *                  contains CPU cores and UMC controllers.
+ */
+enum hygon_node_type {
+	HYGON_NODE_IOD = 0,
+	HYGON_NODE_CDD = 1,
+};
+
+/**
+ * struct hygon_node_info - DF node information
+ * @socket_id: physical socket ID, F1x200[30:28]
+ * @dfid:      Data Fabric ID, model-dependent source
+ * @type:      HYGON_NODE_CDD or HYGON_NODE_IOD
+ */
+struct hygon_node_info {
+	u8			socket_id;
+	u8			dfid;
+	enum hygon_node_type	type;
+};
+
+#ifdef CONFIG_HYGON_NODE
+
+/**
+ * hygon_node_num() - total number of DF nodes (CDD + IOD)
+ *
+ * This is the upper bound for every node index in this API.
+ *
+ * Return: total node count, or 0 if the cache is not ready.
+ */
+u16 hygon_node_num(void);
+
+/**
+ * hygon_cdd_num() - number of Core and DDR Dies (CDD)
+ *
+ * A CDD (DFID >= 4) has CPU cores and UMC controllers; an IOD
+ * (DFID < 4) has neither. CDDs occupy nodes [0, hygon_cdd_num()).
+ *
+ * Return: CDD count, or 0 if the cache is not ready.
+ */
+u16 hygon_cdd_num(void);
+
+/**
+ * hygon_node_get_info() - get information for a DF node
+ * @node: DF node index in [0, hygon_node_num())
+ * @info: output structure (socket_id, dfid, type)
+ *
+ * Return: 0 on success, -EINVAL if @node is out of range or @info is
+ * NULL, -ENODEV if the cache is not ready.
+ */
+int hygon_node_get_info(u16 node, struct hygon_node_info *info);
+
+/**
+ * hygon_node_get_func() - get DF function PCI device for a node
+ * @node: DF node index in [0, hygon_node_num())
+ * @func: HYGON_DF_F3 or HYGON_DF_F4
+ *
+ * Return: referenced pci_dev on success.  NULL if @node is out of
+ * range, @func is unsupported, or the cache is not ready.  The
+ * caller must release the reference with pci_dev_put().
+ */
+struct pci_dev *hygon_node_get_func(u16 node, u8 func);
+
+/**
+ * hygon_pci_dev_to_df_node() - find DF node owning the given PCI device
+ * @pdev: PCI function on the same domain, bus and slot as a cached DF node
+ *
+ * Looks up the DF node whose misc (F3) device shares the same PCI
+ * domain, bus and slot as @pdev.
+ *
+ * Return: DF node index in [0, hygon_node_num()) on success, -EINVAL
+ * if @pdev is NULL, or -ENODEV if no matching node is found or the
+ * cache is not ready.
+ */
+int hygon_pci_dev_to_df_node(struct pci_dev *pdev);
+
+#else /* !CONFIG_HYGON_NODE */
+
+static inline u16 hygon_node_num(void)
+{
+	return 0;
+}
+
+static inline u16 hygon_cdd_num(void)
+{
+	return 0;
+}
+
+static inline int hygon_node_get_info(u16 node, struct hygon_node_info *info)
+{
+	return -ENODEV;
+}
+
+static inline struct pci_dev *hygon_node_get_func(u16 node, u8 func)
+{
+	return NULL;
+}
+
+static inline int hygon_pci_dev_to_df_node(struct pci_dev *pdev)
+{
+	return -ENODEV;
+}
+
+#endif /* CONFIG_HYGON_NODE */
+
+#endif /* _ASM_X86_HYGON_NODE_H */
diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile
index 31f46fd00527..b8430e5e53fb 100644
--- a/arch/x86/kernel/Makefile
+++ b/arch/x86/kernel/Makefile
@@ -136,6 +136,7 @@ obj-$(CONFIG_HPET_TIMER) 	+= hpet.o
 
 obj-$(CONFIG_AMD_NB)		+= amd_nb.o
 obj-$(CONFIG_AMD_NODE)		+= amd_node.o
+obj-$(CONFIG_HYGON_NODE)	+= hygon_node.o
 obj-$(CONFIG_DEBUG_NMI_SELFTEST) += nmi_selftest.o
 
 obj-$(CONFIG_KVM_GUEST)		+= kvm.o kvmclock.o
diff --git a/arch/x86/kernel/hygon_node.c b/arch/x86/kernel/hygon_node.c
new file mode 100644
index 000000000000..9ba734c09e6f
--- /dev/null
+++ b/arch/x86/kernel/hygon_node.c
@@ -0,0 +1,685 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Hygon Family 0x18 Data Fabric node enumeration
+ *
+ * A DF instance exposes sibling PCI functions in one slot. Function 3
+ * is the misc device used for enumeration, function 4 is the link
+ * device, and functions 1 and 5 provide the socket and DF identity.
+ *
+ * Enumerate F3 devices by PCI ID, validate their F4 siblings, read each
+ * node's socket ID and DFID, and validate the enumerated socket set
+ * against SocketPresent. Sort the nodes into a dense cache with Core
+ * and DDR Dies (CDDs) before I/O Dies (IODs), then by socket ID and
+ * DFID.
+ */
+
+#define pr_fmt(fmt) "hygon_node: " fmt
+
+#include <linux/bitops.h>
+#include <linux/cpu.h>
+#include <linux/cpufeature.h>
+#include <linux/export.h>
+#include <linux/init.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <linux/processor.h>
+#include <linux/slab.h>
+#include <linux/sort.h>
+
+#include <asm/cpu_device_id.h>
+#include <asm/hygon/node.h>
+
+/* Maximum socket count this implementation supports. */
+#define HYGON_MAX_SOCKETS	8
+
+/*
+ * DF register offsets used for node identity discovery.
+ *
+ * F1x200 (SystemCfg) -- present on all supported models:
+ *   [30:28]  MySocketId    - hardware socket ID
+ *   [23:20]  MyDieId       - die ID (equals DFID on some models, see below)
+ *
+ * F5x180 (FabricBlockInstanceInformation3_CS) -- used on Model 06h-08h:
+ *   [19:16]  DFID          - Data Fabric ID for UMC/SMN addressing
+ *
+ * DFID source by model:
+ *   Model 04h/05h:  F1x200[23:20] (MyDieId == DFID)
+ *   Model 06h-08h:  F5x180[19:16] (MyDieId != DFID, different numbering)
+ */
+#define DF_F1_SYSTEM_CFG	0x200
+#define DF_F5_FABRIC_ID		0x180
+
+/* DF function numbers for sibling device access (internal use). */
+#define HYGON_DF_F1	1	/* SystemCfg: socket and die identity */
+#define HYGON_DF_F5	5	/* FabricId: DFID on Model 06h-08h */
+
+/* PCI device IDs used only by Hygon DF node discovery. */
+#define PCI_DEVICE_ID_HYGON_18H_DF_F3		0x1463
+#define PCI_DEVICE_ID_HYGON_18H_DF_F4		0x1464
+#define PCI_DEVICE_ID_HYGON_18H_M04H_DF_F1	0x1491
+#define PCI_DEVICE_ID_HYGON_18H_M04H_DF_F3	0x1493
+#define PCI_DEVICE_ID_HYGON_18H_M04H_DF_F4	0x1494
+#define PCI_DEVICE_ID_HYGON_18H_M05H_DF_F1	0x14b1
+#define PCI_DEVICE_ID_HYGON_18H_M05H_DF_F3	0x14b3
+#define PCI_DEVICE_ID_HYGON_18H_M05H_DF_F4	0x14b4
+#define PCI_DEVICE_ID_HYGON_18H_M06H_DF_F5	0x14b5
+
+/*
+ * Cached identity for one DF instance.  After sorting, CDDs occupy
+ * nodes[0..num_cdd-1].
+ *
+ * The PCI BDF is the access point for a DF node, not its identity.
+ * socket_id and dfid, read from DF registers, together identify the
+ * node in hardware.
+ */
+struct hygon_node {
+	struct pci_dev	*misc;		/* DF function 3 */
+	struct pci_dev	*link;		/* DF function 4 */
+	u8		socket_id;	/* F1x200[30:28] */
+	u8		dfid;		/* model-dependent DFID */
+	bool		is_cdd;		/* DFID >= 4 */
+};
+
+struct hygon_node_cache {
+	struct hygon_node	*nodes;		/* sorted: CDD first, then IOD */
+	u16			num_nodes;	/* CDD + IOD = total */
+	u16			num_cdd;	/* CDD only */
+	u16			num_sockets;
+
+	/* Set after DF node collection, sorting and validation complete. */
+	bool			ready;
+};
+
+struct hygon_df_id {
+	u8	socket_id;
+	u8	dfid;
+};
+
+/* DF sibling device IDs used to read node identity. */
+struct hygon_df_cfg {
+	u16	f1_id;
+	u16	f5_id;			/* 0 = not available */
+};
+
+/* DF misc (F3) device IDs for all supported Hygon Family 0x18 models. */
+static const struct pci_device_id hygon_nb_misc_ids[] = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_HYGON, PCI_DEVICE_ID_HYGON_18H_DF_F3) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_HYGON, PCI_DEVICE_ID_HYGON_18H_M04H_DF_F3) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_HYGON, PCI_DEVICE_ID_HYGON_18H_M05H_DF_F3) },
+	{}
+};
+
+/* DF link (F4) device IDs for supported models. */
+static const struct pci_device_id hygon_nb_link_ids[] = {
+	{ PCI_DEVICE(PCI_VENDOR_ID_HYGON, PCI_DEVICE_ID_HYGON_18H_DF_F4) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_HYGON, PCI_DEVICE_ID_HYGON_18H_M04H_DF_F4) },
+	{ PCI_DEVICE(PCI_VENDOR_ID_HYGON, PCI_DEVICE_ID_HYGON_18H_M05H_DF_F4) },
+	{}
+};
+
+static const struct hygon_df_cfg hygon_m04_df_cfg __initconst = {
+	.f1_id	= PCI_DEVICE_ID_HYGON_18H_M04H_DF_F1,
+};
+
+static const struct hygon_df_cfg hygon_m05_df_cfg __initconst = {
+	.f1_id	= PCI_DEVICE_ID_HYGON_18H_M05H_DF_F1,
+};
+
+static const struct hygon_df_cfg hygon_m06_m08_df_cfg __initconst = {
+	.f1_id	= PCI_DEVICE_ID_HYGON_18H_M05H_DF_F1,
+	.f5_id	= PCI_DEVICE_ID_HYGON_18H_M06H_DF_F5,
+};
+
+static const struct x86_cpu_id hygon_df_cpuids[] __initconst = {
+	X86_MATCH_VENDOR_FAM_MODEL(HYGON, 0x18, 0x04, &hygon_m04_df_cfg),
+	X86_MATCH_VENDOR_FAM_MODEL(HYGON, 0x18, 0x05, &hygon_m05_df_cfg),
+	X86_MATCH_VENDOR_FAM_MODEL(HYGON, 0x18, 0x06, &hygon_m06_m08_df_cfg),
+	X86_MATCH_VENDOR_FAM_MODEL(HYGON, 0x18, 0x07, &hygon_m06_m08_df_cfg),
+	X86_MATCH_VENDOR_FAM_MODEL(HYGON, 0x18, 0x08, &hygon_m06_m08_df_cfg),
+	{}
+};
+
+static struct hygon_node_cache hygon_cache;
+
+static void __init hygon_dump_nodes(const struct hygon_node_cache *cache,
+				    const char *phase)
+{
+	u16 i;
+
+	pr_debug("%s: %u nodes\n", phase, cache->num_nodes);
+
+	for (i = 0; i < cache->num_nodes; i++) {
+		const struct hygon_node *node = &cache->nodes[i];
+
+		pr_debug("%s: node[%u] %04x:%02x:%02x.%u socket=%u dfid=%u type=%s\n",
+			 phase, i, pci_domain_nr(node->misc->bus),
+			 node->misc->bus->number,
+			 PCI_SLOT(node->misc->devfn),
+			 PCI_FUNC(node->misc->devfn),
+			 node->socket_id, node->dfid,
+			 node->is_cdd ? "CDD" : "IOD");
+	}
+}
+
+/*
+ * Iterate Hygon PCI devices, returning the next one that matches @ids.
+ * Follows the pci_get_device() convention: @from is consumed (its
+ * reference is dropped) and the returned device has an elevated
+ * reference count.
+ */
+static struct pci_dev * __init next_hygon_dev(struct pci_dev *from,
+					      const struct pci_device_id *ids)
+{
+	while ((from = pci_get_device(PCI_VENDOR_ID_HYGON, PCI_ANY_ID, from))) {
+		if (pci_match_id(ids, from))
+			return from;
+	}
+
+	return NULL;
+}
+
+/*
+ * Find the DF link (function 4) sibling of a DF misc (function 3)
+ * device.  Both functions share the same PCI bus and slot.
+ */
+static struct pci_dev * __init hygon_get_link(struct pci_dev *misc)
+{
+	struct pci_dev *link;
+
+	link = pci_get_domain_bus_and_slot(pci_domain_nr(misc->bus),
+					   misc->bus->number,
+					   PCI_DEVFN(PCI_SLOT(misc->devfn),
+						     HYGON_DF_F4));
+	if (!link)
+		return NULL;
+
+	if (!pci_match_id(hygon_nb_link_ids, link)) {
+		pci_dev_put(link);
+		return NULL;
+	}
+
+	return link;
+}
+
+/* Find the DF configuration for the boot CPU. */
+static const struct hygon_df_cfg * __init hygon_get_df_cfg(void)
+{
+	const struct x86_cpu_id *id = x86_match_cpu(hygon_df_cpuids);
+
+	return id ? (const void *)id->driver_data : NULL;
+}
+
+/*
+ * Read a config register from a DF sibling function on the same PCI
+ * slot as @misc.  Only functions 1 (F1, SystemCfg) and 5 (F5,
+ * FabricId) are supported.
+ */
+static int __init hygon_read_df_reg(struct pci_dev *misc, u8 func,
+				    int offset, u32 *value)
+{
+	const struct hygon_df_cfg *cfg;
+	struct pci_dev *sibling;
+	u16 expected_device;
+	int err;
+
+	cfg = hygon_get_df_cfg();
+	if (!cfg)
+		return -ENODEV;
+
+	if (func == HYGON_DF_F1) {
+		expected_device = cfg->f1_id;
+
+		/*
+		 * Model 05h uses the M04H F1 device ID when F3 does not use
+		 * the M05H device ID.
+		 */
+		if (boot_cpu_data.x86_model == 0x5 &&
+		    misc->device != PCI_DEVICE_ID_HYGON_18H_M05H_DF_F3)
+			expected_device = PCI_DEVICE_ID_HYGON_18H_M04H_DF_F1;
+	} else if (func == HYGON_DF_F5) {
+		expected_device = cfg->f5_id;
+	} else {
+		return -EINVAL;
+	}
+
+	if (!expected_device)
+		return -ENODEV;
+
+	sibling = pci_get_domain_bus_and_slot(pci_domain_nr(misc->bus),
+					      misc->bus->number,
+					      PCI_DEVFN(PCI_SLOT(misc->devfn),
+							func));
+	if (!sibling)
+		return -ENODEV;
+
+	if (sibling->vendor != PCI_VENDOR_ID_HYGON ||
+	    sibling->device != expected_device) {
+		pci_dev_put(sibling);
+		return -ENODEV;
+	}
+
+	err = pci_read_config_dword(sibling, offset, value);
+	pci_dev_put(sibling);
+
+	if (err) {
+		pr_warn("error reading %04x:%02x:%02x.%u offset 0x%x\n",
+			pci_domain_nr(misc->bus), misc->bus->number,
+			PCI_SLOT(misc->devfn), func, offset);
+		return pcibios_err_to_errno(err);
+	}
+
+	/* Do not parse a PCI error response as DF identity. */
+	if (PCI_POSSIBLE_ERROR(*value)) {
+		pr_warn("error response reading %04x:%02x:%02x.%u offset 0x%x\n",
+			pci_domain_nr(misc->bus), misc->bus->number,
+			PCI_SLOT(misc->devfn), func, offset);
+		return -ENODEV;
+	}
+
+	return 0;
+}
+
+/*
+ * Read the hardware identity for one DF misc device from its sibling
+ * functions.
+ *
+ * Supported models expose F1x200 (SystemCfg): socket_id from [30:28] and a
+ * die identifier (MyDieId) from [23:20].
+ * On Model 06h-08h MyDieId differs from the DFID used by UMC and SMN
+ * addressing, so an additional F5x180 (FabricId) read obtains the DFID
+ * from [19:16].
+ *
+ * The boot CPU model selects the DF register layout for all instances.
+ */
+static int __init hygon_read_df_id(struct pci_dev *misc,
+				   struct hygon_df_id *id)
+{
+	const struct hygon_df_cfg *cfg = hygon_get_df_cfg();
+	u32 reg;
+	int ret;
+
+	if (!cfg)
+		return -ENODEV;
+
+	ret = hygon_read_df_reg(misc, HYGON_DF_F1, DF_F1_SYSTEM_CFG, &reg);
+	if (ret)
+		return ret;
+
+	id->socket_id = (reg >> 28) & 0x7;
+	id->dfid      = (reg >> 20) & 0xf;
+
+	/* Read DFID from F5x180 on models that provide an F5 sibling. */
+	if (cfg->f5_id) {
+		ret = hygon_read_df_reg(misc, HYGON_DF_F5, DF_F5_FABRIC_ID,
+					&reg);
+		if (ret)
+			return ret;
+		id->dfid = (reg >> 16) & 0xf;
+	}
+
+	return 0;
+}
+
+/*
+ * Read the system-wide SocketPresent mask from F1x200[7:0].
+ *
+ * SocketPresent is system-wide rather than per-node. Read it once from
+ * the first DF misc device. A zero mask is invalid on supported models.
+ */
+static int __init hygon_read_socket_present(struct pci_dev *misc,
+					    u8 *socket_present)
+{
+	u32 reg;
+	int ret;
+
+	ret = hygon_read_df_reg(misc, HYGON_DF_F1, DF_F1_SYSTEM_CFG, &reg);
+	if (ret)
+		return ret;
+
+	*socket_present = reg & 0xff;
+	if (!*socket_present) {
+		pr_warn("SocketPresent is zero\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void __init hygon_release_nodes(struct hygon_node *nodes, u16 count)
+{
+	u16 i;
+
+	for (i = 0; i < count; i++) {
+		pci_dev_put(nodes[i].misc);
+		pci_dev_put(nodes[i].link);
+	}
+
+	kfree(nodes);
+}
+
+/*
+ * Walk all DF misc (F3) devices and read per-node identity (socket_id,
+ * dfid) from each, collecting them into a flat array.  The system-wide
+ * SocketPresent mask is sampled once, and the enumerated socket set is
+ * validated against it; socket IDs must also be dense (0..N-1).
+ */
+static int __init hygon_collect_nodes(struct hygon_node_cache *cache)
+{
+	struct hygon_node *nodes;
+	struct pci_dev *misc = NULL;
+	struct pci_dev *link = NULL;
+	u16 capacity = 0, count = 0;
+	u8 observed_socket_mask = 0;
+	u8 socket_present_mask = 0;
+	int ret;
+
+	while ((misc = next_hygon_dev(misc, hygon_nb_misc_ids)))
+		capacity++;
+
+	if (!capacity)
+		return -ENODEV;
+
+	nodes = kcalloc(capacity, sizeof(*nodes), GFP_KERNEL);
+	if (!nodes)
+		return -ENOMEM;
+
+	while ((misc = next_hygon_dev(misc, hygon_nb_misc_ids))) {
+		struct hygon_df_id id;
+
+		link = hygon_get_link(misc);
+		if (!link) {
+			ret = -ENODEV;
+			goto fail_put_devs;
+		}
+
+		ret = hygon_read_df_id(misc, &id);
+		if (ret)
+			goto fail_put_devs;
+
+		/* SocketPresent is system-wide, so sample it once. */
+		if (!count) {
+			ret = hygon_read_socket_present(misc,
+							&socket_present_mask);
+			if (ret)
+				goto fail_put_devs;
+		}
+
+		if (count >= capacity) {
+			ret = -ENODEV;
+			goto fail_put_devs;
+		}
+
+		pr_debug("collect: %04x:%02x:%02x.%u socket=%u dfid=%u\n",
+			 pci_domain_nr(misc->bus), misc->bus->number,
+			 PCI_SLOT(misc->devfn), PCI_FUNC(misc->devfn),
+			 id.socket_id, id.dfid);
+
+		nodes[count].misc      = pci_dev_get(misc);
+		nodes[count].link      = link;
+		link = NULL;
+		nodes[count].socket_id = id.socket_id;
+		nodes[count].dfid      = id.dfid;
+		nodes[count].is_cdd    = (id.dfid >= HYGON_CDD_DFID_BASE);
+		count++;
+
+		observed_socket_mask |= BIT(id.socket_id);
+	}
+
+	if (count != capacity) {
+		pr_warn("DF enumeration changed: expected %u nodes, got %u\n",
+			capacity, count);
+		ret = -ENODEV;
+		goto fail;
+	}
+
+	/*
+	 * Every populated socket must contribute at least one enumerated DF
+	 * misc device. socket_present_mask is guaranteed to be non-zero.
+	 */
+	if (observed_socket_mask != socket_present_mask) {
+		pr_warn("SocketPresent mismatch: observed=0x%x present=0x%x\n",
+			observed_socket_mask, socket_present_mask);
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	cache->nodes       = nodes;
+	cache->num_nodes   = count;
+	cache->num_sockets = hweight8(socket_present_mask);
+
+	if (socket_present_mask != GENMASK(cache->num_sockets - 1, 0)) {
+		pr_warn("sparse socket IDs not supported (SocketPresent=0x%x)\n",
+			socket_present_mask);
+		ret = -EINVAL;
+		goto fail;
+	}
+
+	return 0;
+
+fail_put_devs:
+	pci_dev_put(link);
+	pci_dev_put(misc);
+fail:
+	hygon_release_nodes(nodes, count);
+	cache->nodes = NULL;
+	cache->num_nodes = 0;
+	cache->num_sockets = 0;
+	return ret;
+}
+
+/*
+ * Sort CDD nodes before IOD nodes, then order by hardware
+ * (socket_id, dfid).
+ */
+static int __init hygon_node_cmp(const void *a, const void *b)
+{
+	const struct hygon_node *left = a;
+	const struct hygon_node *right = b;
+
+	if (left->is_cdd != right->is_cdd)
+		return right->is_cdd - left->is_cdd;
+
+	if (left->socket_id != right->socket_id)
+		return (int)left->socket_id - right->socket_id;
+
+	return (int)left->dfid - (int)right->dfid;
+}
+
+/*
+ * Classify the sorted node array and validate that every socket has the
+ * same number of CDDs.
+ */
+static int __init hygon_sort_and_classify(struct hygon_node_cache *cache)
+{
+	u16 cdd_per_socket;
+	u16 i;
+	u8 per_sock_count[HYGON_MAX_SOCKETS] = { 0 };
+
+	hygon_dump_nodes(cache, "before-sort");
+
+	sort(cache->nodes, cache->num_nodes, sizeof(*cache->nodes),
+	     hygon_node_cmp, NULL);
+
+	for (i = 1; i < cache->num_nodes; i++) {
+		const struct hygon_node *prev = &cache->nodes[i - 1];
+		const struct hygon_node *node = &cache->nodes[i];
+
+		if (node->socket_id == prev->socket_id &&
+		    node->dfid == prev->dfid) {
+			pr_warn("duplicate DF node: socket=%u dfid=%u\n",
+				node->socket_id, node->dfid);
+			return -EINVAL;
+		}
+	}
+
+	for (i = 0; i < cache->num_nodes; i++) {
+		if (!cache->nodes[i].is_cdd)
+			break;
+	}
+
+	cache->num_cdd = i;
+
+	if (!cache->num_cdd)
+		return -ENODEV;
+
+	if (cache->num_cdd % cache->num_sockets) {
+		pr_warn("CDD count %u not divisible by %u sockets\n",
+			cache->num_cdd, cache->num_sockets);
+		return -EINVAL;
+	}
+
+	cdd_per_socket = cache->num_cdd / cache->num_sockets;
+	if (!cdd_per_socket)
+		return -EINVAL;
+
+	for (i = 0; i < cache->num_cdd; i++) {
+		u8 socket_id = cache->nodes[i].socket_id;
+
+		if (socket_id >= cache->num_sockets)
+			return -EINVAL;
+		per_sock_count[socket_id]++;
+	}
+
+	for (i = 0; i < cache->num_sockets; i++) {
+		if (per_sock_count[i] != cdd_per_socket) {
+			pr_warn("socket %u: %u CDDs, expected %u\n",
+				i, per_sock_count[i], cdd_per_socket);
+			return -EINVAL;
+		}
+	}
+
+	hygon_dump_nodes(cache, "after-sort");
+
+	return 0;
+}
+
+/*
+ * Build the global DF node cache.
+ *
+ * Called once from hygon_node_init() at fs_initcall, so no locking is
+ * required. Set ready after node collection and sorting.
+ */
+static int __init hygon_build_cache(void)
+{
+	int err;
+
+	err = hygon_collect_nodes(&hygon_cache);
+	if (err)
+		return err;
+
+	err = hygon_sort_and_classify(&hygon_cache);
+	if (err)
+		goto fail;
+
+	hygon_cache.ready = true;
+
+	return 0;
+
+fail:
+	hygon_release_nodes(hygon_cache.nodes, hygon_cache.num_nodes);
+	hygon_cache.nodes = NULL;
+	hygon_cache.num_nodes = 0;
+	hygon_cache.num_cdd = 0;
+	hygon_cache.num_sockets = 0;
+	return err;
+}
+
+u16 hygon_node_num(void)
+{
+	return hygon_cache.ready ? hygon_cache.num_nodes : 0;
+}
+EXPORT_SYMBOL_GPL(hygon_node_num);
+
+u16 hygon_cdd_num(void)
+{
+	return hygon_cache.ready ? hygon_cache.num_cdd : 0;
+}
+EXPORT_SYMBOL_GPL(hygon_cdd_num);
+
+int hygon_node_get_info(u16 node, struct hygon_node_info *info)
+{
+	const struct hygon_node *n;
+
+	if (!info)
+		return -EINVAL;
+
+	if (!hygon_cache.ready)
+		return -ENODEV;
+
+	if (node >= hygon_cache.num_nodes)
+		return -EINVAL;
+
+	n = &hygon_cache.nodes[node];
+	info->socket_id = n->socket_id;
+	info->dfid      = n->dfid;
+	info->type      = n->is_cdd ? HYGON_NODE_CDD : HYGON_NODE_IOD;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(hygon_node_get_info);
+
+struct pci_dev *hygon_node_get_func(u16 node, u8 func)
+{
+	if (!hygon_cache.ready)
+		return NULL;
+
+	if (node >= hygon_cache.num_nodes)
+		return NULL;
+
+	switch (func) {
+	case HYGON_DF_F3:
+		return pci_dev_get(hygon_cache.nodes[node].misc);
+	case HYGON_DF_F4:
+		return pci_dev_get(hygon_cache.nodes[node].link);
+	default:
+		return NULL;
+	}
+}
+EXPORT_SYMBOL_GPL(hygon_node_get_func);
+
+int hygon_pci_dev_to_df_node(struct pci_dev *pdev)
+{
+	u16 i;
+
+	if (!pdev)
+		return -EINVAL;
+
+	if (!hygon_cache.ready)
+		return -ENODEV;
+
+	for (i = 0; i < hygon_cache.num_nodes; i++) {
+		struct pci_dev *misc = hygon_cache.nodes[i].misc;
+
+		if (pci_domain_nr(misc->bus) == pci_domain_nr(pdev->bus) &&
+		    misc->bus->number       == pdev->bus->number      &&
+		    PCI_SLOT(misc->devfn)   == PCI_SLOT(pdev->devfn))
+			return i;
+	}
+
+	return -ENODEV;
+}
+EXPORT_SYMBOL_GPL(hygon_pci_dev_to_df_node);
+
+/*
+ * Build the Hygon DF node cache at fs_initcall.
+ */
+static int __init hygon_node_init(void)
+{
+	int ret;
+
+	if (!hygon_get_df_cfg())
+		return 0;
+
+	ret = hygon_build_cache();
+	if (ret) {
+		pr_warn("DF node cache build failed: %d\n", ret);
+		return ret;
+	}
+
+	pr_info("%u DF nodes (%u CDDs, %u IODs) across %u sockets\n",
+		hygon_cache.num_nodes, hygon_cache.num_cdd,
+		hygon_cache.num_nodes - hygon_cache.num_cdd,
+		hygon_cache.num_sockets);
+
+	return 0;
+}
+fs_initcall(hygon_node_init);
-- 
2.43.0


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

* [PATCH v5 2/8] x86/hygon: Map CPU NodeIds to DF nodes
  2026-09-10  8:27 [PATCH v5 0/8] x86/hygon: Add Family 0x18 DF node enumeration and SMN access Lin Wang
  2026-09-10  8:27 ` [PATCH v5 1/8] x86/hygon: Add Family 0x18 DF node enumeration Lin Wang
@ 2026-09-10  8:27 ` Lin Wang
  2026-09-10  8:32   ` sashiko-bot
  2026-09-10  8:27 ` [PATCH v5 3/8] EDAC/amd64: Stop matching Hygon CPUs Lin Wang
                   ` (5 subsequent siblings)
  7 siblings, 1 reply; 19+ messages in thread
From: Lin Wang @ 2026-09-10  8:27 UTC (permalink / raw)
  To: Borislav Petkov, Yazen Ghannam, Mario Limonciello
  Cc: Pu Wen, Thomas Gleixner, Ingo Molnar, Dave Hansen, H. Peter Anvin,
	Tony Luck, Clemens Ladisch, Guenter Roeck, x86, linux-edac,
	linux-hwmon, linux-kernel

On bare-metal Hygon systems, CPUID 0x8000001E ECX[7:0] encodes a
CPU's socket and local CDD ordinal. The value is sparse across sockets
and cannot be used as an index into the dense DF node cache.

Translate the CPUID value through the CDDs sorted by socket and DFID.
The NodeId reported by CPUID in a guest may not describe the physical
DF topology. Return -ENODEV in guests or when the NodeId does not
identify an enumerated CDD.

Signed-off-by: Lin Wang <wanglin@open-hieco.net>
---
 arch/x86/include/asm/hygon/node.h | 22 +++++++++++++
 arch/x86/kernel/hygon_node.c      | 51 +++++++++++++++++++++++++++++++
 2 files changed, 73 insertions(+)

diff --git a/arch/x86/include/asm/hygon/node.h b/arch/x86/include/asm/hygon/node.h
index 77e9f5354837..8ed44addedd4 100644
--- a/arch/x86/include/asm/hygon/node.h
+++ b/arch/x86/include/asm/hygon/node.h
@@ -77,6 +77,23 @@ u16 hygon_cdd_num(void);
  */
 int hygon_node_get_info(u16 node, struct hygon_node_info *info);
 
+/**
+ * hygon_cpu_to_df_node() - map CPU to dense DF CDD index
+ * @cpu: CPU index
+ *
+ * Hygon Fam18h exposes sparse physical node IDs via CPUID 0x8000001E
+ * ECX[7:0].
+ * This function translates the per-CPU physical node ID into a dense
+ * DF CDD index in [0, hygon_cdd_num()). The NodeId reported by CPUID in
+ * a guest may not describe the physical DF topology, so the translation
+ * is unavailable in guests.
+ *
+ * Return: DF CDD index on success, -EINVAL if @cpu is out of range,
+ * -ENODEV if CPU-to-DF mapping is unavailable or the physical node ID
+ * does not map to a known DF node.
+ */
+int hygon_cpu_to_df_node(unsigned int cpu);
+
 /**
  * hygon_node_get_func() - get DF function PCI device for a node
  * @node: DF node index in [0, hygon_node_num())
@@ -118,6 +135,11 @@ static inline int hygon_node_get_info(u16 node, struct hygon_node_info *info)
 	return -ENODEV;
 }
 
+static inline int hygon_cpu_to_df_node(unsigned int cpu)
+{
+	return -ENODEV;
+}
+
 static inline struct pci_dev *hygon_node_get_func(u16 node, u8 func)
 {
 	return NULL;
diff --git a/arch/x86/kernel/hygon_node.c b/arch/x86/kernel/hygon_node.c
index 9ba734c09e6f..a7d09a3938c3 100644
--- a/arch/x86/kernel/hygon_node.c
+++ b/arch/x86/kernel/hygon_node.c
@@ -25,6 +25,7 @@
 #include <linux/processor.h>
 #include <linux/slab.h>
 #include <linux/sort.h>
+#include <linux/topology.h>
 
 #include <asm/cpu_device_id.h>
 #include <asm/hygon/node.h>
@@ -553,6 +554,40 @@ static int __init hygon_sort_and_classify(struct hygon_node_cache *cache)
 	return 0;
 }
 
+/*
+ * Translate a Hygon Fam18h phys_node_id (CPUID 8000001E ECX[7:0]) to a
+ * dense DF CDD index.  On supported models, CPUID reports this
+ * encoding:
+ *
+ *   phys_node_id = (socket_id << 4) | local_cdd_index_in_dfid_order
+ *
+ * The cache->nodes[] CDD region is sorted by (socket_id ASC, dfid ASC),
+ * so walk it and return the actual cache index of the CDD whose
+ * socket_id matches and whose socket-local ordinal is @local.
+ *
+ * Return -ENODEV if the cache is unavailable or no CDD matches.
+ */
+static int hygon_phys_nid_to_df_node(unsigned int phys_nid)
+{
+	unsigned int socket = phys_nid >> 4;
+	unsigned int local = phys_nid & 0xf;
+	unsigned int ordinal = 0;
+	u16 i;
+
+	if (!hygon_cache.ready)
+		return -ENODEV;
+
+	for (i = 0; i < hygon_cache.num_cdd; i++) {
+		if (hygon_cache.nodes[i].socket_id != socket)
+			continue;
+
+		if (ordinal++ == local)
+			return i;
+	}
+
+	return -ENODEV;
+}
+
 /*
  * Build the global DF node cache.
  *
@@ -617,6 +652,22 @@ int hygon_node_get_info(u16 node, struct hygon_node_info *info)
 }
 EXPORT_SYMBOL_GPL(hygon_node_get_info);
 
+int hygon_cpu_to_df_node(unsigned int cpu)
+{
+	if (cpu >= nr_cpu_ids)
+		return -EINVAL;
+
+	/*
+	 * The NodeId reported by CPUID in a guest may not describe the
+	 * physical DF topology.
+	 */
+	if (cpu_feature_enabled(X86_FEATURE_HYPERVISOR))
+		return -ENODEV;
+
+	return hygon_phys_nid_to_df_node(topology_amd_node_id(cpu));
+}
+EXPORT_SYMBOL_GPL(hygon_cpu_to_df_node);
+
 struct pci_dev *hygon_node_get_func(u16 node, u8 func)
 {
 	if (!hygon_cache.ready)
-- 
2.43.0


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

* [PATCH v5 3/8] EDAC/amd64: Stop matching Hygon CPUs
  2026-09-10  8:27 [PATCH v5 0/8] x86/hygon: Add Family 0x18 DF node enumeration and SMN access Lin Wang
  2026-09-10  8:27 ` [PATCH v5 1/8] x86/hygon: Add Family 0x18 DF node enumeration Lin Wang
  2026-09-10  8:27 ` [PATCH v5 2/8] x86/hygon: Map CPU NodeIds to DF nodes Lin Wang
@ 2026-09-10  8:27 ` Lin Wang
  2026-09-10  8:33   ` sashiko-bot
  2026-09-10  8:27 ` [PATCH v5 4/8] RAS/AMD/ATL: Match AMD CPUs only Lin Wang
                   ` (4 subsequent siblings)
  7 siblings, 1 reply; 19+ messages in thread
From: Lin Wang @ 2026-09-10  8:27 UTC (permalink / raw)
  To: Borislav Petkov, Yazen Ghannam, Mario Limonciello
  Cc: Pu Wen, Thomas Gleixner, Ingo Molnar, Dave Hansen, H. Peter Anvin,
	Tony Luck, Clemens Ladisch, Guenter Roeck, x86, linux-edac,
	linux-hwmon, linux-kernel

amd64_edac matches Hygon Family 0x18, but obtains its node count and
F3 devices from the AMD northbridge cache. That cache maps dense node
indices to fixed PCI slots starting at 0x18. Hygon discovers DF devices
by PCI ID and reads their identities from DF registers. Their PCI slots
do not encode node identity, and CPU NodeIds are sparse across sockets.
The cache therefore cannot represent the Hygon topology.

A failed fixed-slot F3 lookup causes amd_cache_northbridges() to discard
the cache, and amd64_edac then exits at its amd_nb_num() check. The
driver has no valid Hygon node count or node-to-F3 mapping.

Remove the Hygon CPU match and the unused Family 0x18 case. Hygon memory
controller support requires the Hygon node interfaces and will be added
separately. This does not affect MCE decoding in mce_amd.c.

Signed-off-by: Lin Wang <wanglin@open-hieco.net>
---
 drivers/edac/amd64_edac.c | 4 ----
 1 file changed, 4 deletions(-)

diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c
index 475235c402e8..9e35098d3dd2 100644
--- a/drivers/edac/amd64_edac.c
+++ b/drivers/edac/amd64_edac.c
@@ -3840,9 +3840,6 @@ static int per_family_init(struct amd64_pvt *pvt)
 		}
 		break;
 
-	case 0x18:
-		break;
-
 	case 0x19:
 		switch (pvt->model) {
 		case 0x00 ... 0x0f:
@@ -4120,7 +4117,6 @@ static const struct x86_cpu_id amd64_cpuids[] = {
 	X86_MATCH_VENDOR_FAM(AMD,	0x15, NULL),
 	X86_MATCH_VENDOR_FAM(AMD,	0x16, NULL),
 	X86_MATCH_VENDOR_FAM(AMD,	0x17, NULL),
-	X86_MATCH_VENDOR_FAM(HYGON,	0x18, NULL),
 	X86_MATCH_VENDOR_FAM(AMD,	0x19, NULL),
 	X86_MATCH_VENDOR_FAM(AMD,	0x1A, NULL),
 	{ }
-- 
2.43.0


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

* [PATCH v5 4/8] RAS/AMD/ATL: Match AMD CPUs only
  2026-09-10  8:27 [PATCH v5 0/8] x86/hygon: Add Family 0x18 DF node enumeration and SMN access Lin Wang
                   ` (2 preceding siblings ...)
  2026-09-10  8:27 ` [PATCH v5 3/8] EDAC/amd64: Stop matching Hygon CPUs Lin Wang
@ 2026-09-10  8:27 ` Lin Wang
  2026-09-10  8:34   ` sashiko-bot
  2026-09-10  8:27 ` [PATCH v5 5/8] hwmon: (k10temp) Stop matching Hygon devices Lin Wang
                   ` (3 subsequent siblings)
  7 siblings, 1 reply; 19+ messages in thread
From: Lin Wang @ 2026-09-10  8:27 UTC (permalink / raw)
  To: Borislav Petkov, Yazen Ghannam, Mario Limonciello
  Cc: Pu Wen, Thomas Gleixner, Ingo Molnar, Dave Hansen, H. Peter Anvin,
	Tony Luck, Clemens Ladisch, Guenter Roeck, x86, linux-edac,
	linux-hwmon, linux-kernel

The ATL CPU table matches Hygon through X86_FEATURE_SMCA and
X86_FEATURE_ZEN, but the library obtains its node count and F4 devices
from the AMD northbridge cache. That cache maps dense node indices to
fixed PCI slots. Hygon PCI slots do not encode node identity, and CPU
NodeIds are sparse across sockets. The cache therefore cannot represent
the Hygon topology or provide ATL with a valid node-to-F4 mapping.

A failed fixed-slot F3 lookup leaves the cache empty, so amd_atl_init()
exits at its amd_nb_num() check.

Limit both feature matches to AMD. Hygon address translation requires
the Hygon node interfaces and will be added separately.

Signed-off-by: Lin Wang <wanglin@open-hieco.net>
---
 drivers/ras/amd/atl/core.c | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/drivers/ras/amd/atl/core.c b/drivers/ras/amd/atl/core.c
index d77dacdd4f56..fe84fc52c657 100644
--- a/drivers/ras/amd/atl/core.c
+++ b/drivers/ras/amd/atl/core.c
@@ -176,18 +176,17 @@ static void check_for_legacy_df_access(void)
 }
 
 /*
- * This library provides functionality for AMD-based systems with a Data Fabric.
+ * This library provides functionality for AMD systems with a Data Fabric.
  * The set of systems with a Data Fabric is equivalent to the set of Zen-based systems
  * and the set of systems with the Scalable MCA feature at this time. However, these
  * are technically independent things.
  *
  * It's possible to match on the PCI IDs of the Data Fabric devices, but this will be
- * an ever expanding list. Instead, match on the SMCA and Zen features to cover all
- * relevant systems.
+ * an ever expanding list. Instead, match AMD CPUs with the SMCA and Zen features.
  */
 static const struct x86_cpu_id amd_atl_cpuids[] = {
-	X86_MATCH_FEATURE(X86_FEATURE_SMCA, NULL),
-	X86_MATCH_FEATURE(X86_FEATURE_ZEN, NULL),
+	X86_MATCH_VENDOR_FEATURE(AMD, X86_FEATURE_SMCA, NULL),
+	X86_MATCH_VENDOR_FEATURE(AMD, X86_FEATURE_ZEN, NULL),
 	{ }
 };
 
-- 
2.43.0


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

* [PATCH v5 5/8] hwmon: (k10temp) Stop matching Hygon devices
  2026-09-10  8:27 [PATCH v5 0/8] x86/hygon: Add Family 0x18 DF node enumeration and SMN access Lin Wang
                   ` (3 preceding siblings ...)
  2026-09-10  8:27 ` [PATCH v5 4/8] RAS/AMD/ATL: Match AMD CPUs only Lin Wang
@ 2026-09-10  8:27 ` Lin Wang
  2026-09-10  8:36   ` sashiko-bot
  2026-09-11  0:52   ` Guenter Roeck
  2026-09-10  8:27 ` [PATCH v5 6/8] x86/amd_nb: Restrict the northbridge framework to AMD CPUs Lin Wang
                   ` (2 subsequent siblings)
  7 siblings, 2 replies; 19+ messages in thread
From: Lin Wang @ 2026-09-10  8:27 UTC (permalink / raw)
  To: Borislav Petkov, Yazen Ghannam, Mario Limonciello
  Cc: Pu Wen, Thomas Gleixner, Ingo Molnar, Dave Hansen, H. Peter Anvin,
	Tony Luck, Clemens Ladisch, Guenter Roeck, x86, linux-edac,
	linux-hwmon, linux-kernel

k10temp matches the Hygon F3 device and uses the AMD SMN accessors.
These accessors derive an SMN node number by subtracting PCI slot 0x18
from the F3 slot, then pass the result to amd_smn_read(). Hygon PCI
slots do not encode DF node identity, and their SMN roots are grouped
by socket.

The derived node number therefore has no defined relationship to the
Hygon DF node or its SMN root.

Remove the Hygon PCI match and Family 0x18 branch. Hygon temperature
monitoring requires the Hygon node and SMN interfaces and will be added
separately.

Signed-off-by: Lin Wang <wanglin@open-hieco.net>
---
 drivers/hwmon/k10temp.c | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/drivers/hwmon/k10temp.c b/drivers/hwmon/k10temp.c
index 75a45010d687..20a5470e6593 100644
--- a/drivers/hwmon/k10temp.c
+++ b/drivers/hwmon/k10temp.c
@@ -475,7 +475,7 @@ static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
 		data->read_tempreg = read_tempreg_pci;
 	}
 
-	if (boot_cpu_data.x86 == 0x17 || boot_cpu_data.x86 == 0x18) {
+	if (boot_cpu_data.x86 == 0x17) {
 		switch (boot_cpu_data.x86_model) {
 		case 0x1:	/* Zen */
 		case 0x8:	/* Zen+ */
@@ -583,7 +583,6 @@ static const struct pci_device_id k10temp_id_table[] = {
 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_1AH_M60H_DF_F3) },
 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_1AH_M70H_DF_F3) },
 	{ PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_1AH_M90H_DF_F3) },
-	{ PCI_VDEVICE(HYGON, PCI_DEVICE_ID_AMD_17H_DF_F3) },
 	{}
 };
 MODULE_DEVICE_TABLE(pci, k10temp_id_table);
-- 
2.43.0


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

* [PATCH v5 6/8] x86/amd_nb: Restrict the northbridge framework to AMD CPUs
  2026-09-10  8:27 [PATCH v5 0/8] x86/hygon: Add Family 0x18 DF node enumeration and SMN access Lin Wang
                   ` (4 preceding siblings ...)
  2026-09-10  8:27 ` [PATCH v5 5/8] hwmon: (k10temp) Stop matching Hygon devices Lin Wang
@ 2026-09-10  8:27 ` Lin Wang
  2026-09-10  8:37   ` sashiko-bot
  2026-09-10  8:27 ` [PATCH v5 7/8] x86/amd_node: Restrict SMN setup " Lin Wang
  2026-09-10  8:27 ` [PATCH v5 8/8] x86/hygon: Add Family 0x18 SMN access Lin Wang
  7 siblings, 1 reply; 19+ messages in thread
From: Lin Wang @ 2026-09-10  8:27 UTC (permalink / raw)
  To: Borislav Petkov, Yazen Ghannam, Mario Limonciello
  Cc: Pu Wen, Thomas Gleixner, Ingo Molnar, Dave Hansen, H. Peter Anvin,
	Tony Luck, Clemens Ladisch, Guenter Roeck, x86, linux-edac,
	linux-hwmon, linux-kernel

amd_cache_northbridges() obtains function 3 and function 4 for node N
from fixed PCI slot 0x18 + N. Hygon DF devices are discovered by PCI
ID and their slots do not encode node identity, so this cache cannot
represent the Hygon topology.

The preceding patches remove the Hygon matches from amd64_edac and ATL.
Restrict the northbridge initialization and early device check to AMD
CPUs. Keep amd_get_mmconfig_range() available on Hygon because it reads
the MMCONFIG MSR without using the northbridge cache.

Reviewed-by: Yazen Ghannam <yazen.ghannam@amd.com>
Signed-off-by: Lin Wang <wanglin@open-hieco.net>
---
 arch/x86/kernel/amd_nb.c | 6 ++----
 1 file changed, 2 insertions(+), 4 deletions(-)

diff --git a/arch/x86/kernel/amd_nb.c b/arch/x86/kernel/amd_nb.c
index 5d364540673d..848ed2ca17c3 100644
--- a/arch/x86/kernel/amd_nb.c
+++ b/arch/x86/kernel/amd_nb.c
@@ -125,8 +125,7 @@ bool __init early_is_amd_nb(u32 device)
 	const struct pci_device_id *id;
 	u32 vendor = device & 0xffff;
 
-	if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD &&
-	    boot_cpu_data.x86_vendor != X86_VENDOR_HYGON)
+	if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD)
 		return false;
 
 	if (cpu_feature_enabled(X86_FEATURE_ZEN))
@@ -315,8 +314,7 @@ static __init void fix_erratum_688(void)
 
 static __init int init_amd_nbs(void)
 {
-	if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD &&
-	    boot_cpu_data.x86_vendor != X86_VENDOR_HYGON)
+	if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD)
 		return 0;
 
 	amd_cache_northbridges();
-- 
2.43.0


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

* [PATCH v5 7/8] x86/amd_node: Restrict SMN setup to AMD CPUs
  2026-09-10  8:27 [PATCH v5 0/8] x86/hygon: Add Family 0x18 DF node enumeration and SMN access Lin Wang
                   ` (5 preceding siblings ...)
  2026-09-10  8:27 ` [PATCH v5 6/8] x86/amd_nb: Restrict the northbridge framework to AMD CPUs Lin Wang
@ 2026-09-10  8:27 ` Lin Wang
  2026-09-10  8:43   ` sashiko-bot
  2026-09-10  8:27 ` [PATCH v5 8/8] x86/hygon: Add Family 0x18 SMN access Lin Wang
  7 siblings, 1 reply; 19+ messages in thread
From: Lin Wang @ 2026-09-10  8:27 UTC (permalink / raw)
  To: Borislav Petkov, Yazen Ghannam, Mario Limonciello
  Cc: Pu Wen, Thomas Gleixner, Ingo Molnar, Dave Hansen, H. Peter Anvin,
	Tony Luck, Clemens Ladisch, Guenter Roeck, x86, linux-edac,
	linux-hwmon, linux-kernel

amd_smn_init() divides host bridge roots among amd_num_nodes() entries
in PCI enumeration order. Hygon DF nodes instead share one SMN ingress
per socket, so the AMD root map cannot provide Hygon SMN access.

The preceding patches remove the Hygon matches from amd64_edac, ATL and
k10temp. Restrict AMD SMN setup and root discovery to AMD CPUs, leaving
the Hygon roots available for the Hygon node layer.

Signed-off-by: Lin Wang <wanglin@open-hieco.net>
---
 arch/x86/kernel/amd_node.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/arch/x86/kernel/amd_node.c b/arch/x86/kernel/amd_node.c
index 762585775b5a..a7190cec6995 100644
--- a/arch/x86/kernel/amd_node.c
+++ b/arch/x86/kernel/amd_node.c
@@ -229,8 +229,7 @@ static struct pci_dev *get_next_root(struct pci_dev *root)
 		if (root->devfn)
 			continue;
 
-		if (root->vendor != PCI_VENDOR_ID_AMD &&
-		    root->vendor != PCI_VENDOR_ID_HYGON)
+		if (root->vendor != PCI_VENDOR_ID_AMD)
 			continue;
 
 		break;
@@ -253,6 +252,9 @@ static int __init amd_smn_init(void)
 	u16 count, num_roots, roots_per_node, node, num_nodes;
 	struct pci_dev *root;
 
+	if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD)
+		return 0;
+
 	if (!cpu_feature_enabled(X86_FEATURE_ZEN))
 		return 0;
 
-- 
2.43.0


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

* [PATCH v5 8/8] x86/hygon: Add Family 0x18 SMN access
  2026-09-10  8:27 [PATCH v5 0/8] x86/hygon: Add Family 0x18 DF node enumeration and SMN access Lin Wang
                   ` (6 preceding siblings ...)
  2026-09-10  8:27 ` [PATCH v5 7/8] x86/amd_node: Restrict SMN setup " Lin Wang
@ 2026-09-10  8:27 ` Lin Wang
  2026-09-10  8:38   ` sashiko-bot
  7 siblings, 1 reply; 19+ messages in thread
From: Lin Wang @ 2026-09-10  8:27 UTC (permalink / raw)
  To: Borislav Petkov, Yazen Ghannam, Mario Limonciello
  Cc: Pu Wen, Thomas Gleixner, Ingo Molnar, Dave Hansen, H. Peter Anvin,
	Tony Luck, Clemens Ladisch, Guenter Roeck, x86, linux-edac,
	linux-hwmon, linux-kernel

Hygon Family 0x18 DF nodes in a socket share an SMN ingress. The root
groups therefore need to be associated with sockets before they can be
expanded to the dense DF node indices.

Build the node-to-root map after DF enumeration and expose Hygon SMN
read and write helpers. Hold a reference and reserve PCI config space
for every root used by the built-in node layer. SMN remains unavailable
if root discovery or mapping fails.

Signed-off-by: Lin Wang <wanglin@open-hieco.net>
---
 arch/x86/include/asm/hygon/node.h |  40 ++++-
 arch/x86/kernel/hygon_node.c      | 248 +++++++++++++++++++++++++++++-
 2 files changed, 284 insertions(+), 4 deletions(-)

diff --git a/arch/x86/include/asm/hygon/node.h b/arch/x86/include/asm/hygon/node.h
index 8ed44addedd4..24138e9d33e7 100644
--- a/arch/x86/include/asm/hygon/node.h
+++ b/arch/x86/include/asm/hygon/node.h
@@ -2,8 +2,8 @@
 /*
  * Hygon Family 0x18 Data Fabric node enumeration API
  *
- * This header declares Hygon Fam18h DF node enumeration and function
- * access interfaces.
+ * DF node identity, CPU-to-DF-node translation, DF function lookup and
+ * SMN access for Hygon Family 0x18.
  */
 #ifndef _ASM_X86_HYGON_NODE_H
 #define _ASM_X86_HYGON_NODE_H
@@ -118,6 +118,30 @@ struct pci_dev *hygon_node_get_func(u16 node, u8 func);
  */
 int hygon_pci_dev_to_df_node(struct pci_dev *pdev);
 
+/**
+ * hygon_smn_read() - read a 32-bit value from a Hygon SMN address
+ * @node: DF node index in [0, hygon_node_num())
+ * @address: SMN address
+ * @value: output value
+ *
+ * Return: 0 on success, -EINVAL if @value is NULL, -ENODEV if Hygon
+ * SMN is not initialized or @node is out of range, or a negative
+ * errno from the underlying PCI config access.
+ */
+int __must_check hygon_smn_read(u16 node, u32 address, u32 *value);
+
+/**
+ * hygon_smn_write() - write a 32-bit value to a Hygon SMN address
+ * @node: DF node index in [0, hygon_node_num())
+ * @address: SMN address
+ * @value: value to write
+ *
+ * Return: 0 on success, -ENODEV if Hygon SMN is not initialized or
+ * @node is out of range, or a negative errno from the underlying
+ * PCI config access.
+ */
+int __must_check hygon_smn_write(u16 node, u32 address, u32 value);
+
 #else /* !CONFIG_HYGON_NODE */
 
 static inline u16 hygon_node_num(void)
@@ -150,6 +174,18 @@ static inline int hygon_pci_dev_to_df_node(struct pci_dev *pdev)
 	return -ENODEV;
 }
 
+static inline int __must_check hygon_smn_read(u16 node, u32 address,
+					      u32 *value)
+{
+	return -ENODEV;
+}
+
+static inline int __must_check hygon_smn_write(u16 node, u32 address,
+					       u32 value)
+{
+	return -ENODEV;
+}
+
 #endif /* CONFIG_HYGON_NODE */
 
 #endif /* _ASM_X86_HYGON_NODE_H */
diff --git a/arch/x86/kernel/hygon_node.c b/arch/x86/kernel/hygon_node.c
index a7d09a3938c3..e98b0583f363 100644
--- a/arch/x86/kernel/hygon_node.c
+++ b/arch/x86/kernel/hygon_node.c
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: GPL-2.0-only
 /*
- * Hygon Family 0x18 Data Fabric node enumeration
+ * Hygon Family 0x18 Data Fabric node enumeration and SMN access
  *
  * A DF instance exposes sibling PCI functions in one slot. Function 3
  * is the misc device used for enumeration, function 4 is the link
@@ -16,10 +16,12 @@
 #define pr_fmt(fmt) "hygon_node: " fmt
 
 #include <linux/bitops.h>
+#include <linux/cleanup.h>
 #include <linux/cpu.h>
 #include <linux/cpufeature.h>
 #include <linux/export.h>
 #include <linux/init.h>
+#include <linux/mutex.h>
 #include <linux/pci.h>
 #include <linux/pci_ids.h>
 #include <linux/processor.h>
@@ -711,8 +713,246 @@ int hygon_pci_dev_to_df_node(struct pci_dev *pdev)
 EXPORT_SYMBOL_GPL(hygon_pci_dev_to_df_node);
 
 /*
- * Build the Hygon DF node cache at fs_initcall.
+ * SMN index/data register pair offsets in the host-bridge PCI config
+ * space.  Reads and writes to a (node, address) pair are issued as a
+ * two-step transaction: write the SMN address to the index register,
+ * then read or write the value at the data register.
  */
+#define HYGON_SMN_INDEX_OFFSET	0x60
+#define HYGON_SMN_DATA_OFFSET	0x64
+
+/*
+ * Runtime SMN state. hygon_smn_exclusive remains false until setup
+ * succeeds. hygon_smn_reserved_roots owns the independent PCI references
+ * and config-space reservations for the lifetime of the built-in node
+ * layer; hygon_smn_roots contains per-node aliases.
+ */
+static struct pci_dev	**hygon_smn_roots;
+static struct pci_dev	**hygon_smn_reserved_roots;
+static u16		hygon_smn_num_nodes;
+static bool		hygon_smn_exclusive;
+static DEFINE_MUTEX(hygon_smn_mutex);
+
+/* Internal cache accessors used by SMN setup. */
+static u8 __init hygon_node_socket(u16 node)
+{
+	if (!hygon_cache.ready || node >= hygon_cache.num_nodes)
+		return U8_MAX;
+	return hygon_cache.nodes[node].socket_id;
+}
+
+static u16 __init hygon_socket_num(void)
+{
+	return hygon_cache.ready ? hygon_cache.num_sockets : 0;
+}
+
+/*
+ * Walk PCI host-bridge devices matching the Hygon vendor. The SMN
+ * index/data registers live in function 0 of each root complex. This
+ * follows pci_get_class() iterator semantics: @root is consumed and the
+ * returned device has an iterator reference. A retained device needs an
+ * independent reference before the iterator advances.
+ */
+static struct pci_dev * __init hygon_get_next_root(struct pci_dev *root)
+{
+	while ((root = pci_get_class(PCI_CLASS_BRIDGE_HOST << 8, root))) {
+		if (root->devfn)
+			continue;
+		if (root->vendor != PCI_VENDOR_ID_HYGON)
+			continue;
+		break;
+	}
+	return root;
+}
+
+/* Release each config region before dropping its owning device reference. */
+static void __init hygon_release_reserved_roots(struct pci_dev **roots,
+						u16 count)
+{
+	u16 i;
+
+	for (i = 0; i < count; i++) {
+		pci_release_config_region(roots[i], 0, PCI_CFG_SPACE_SIZE);
+		pci_dev_put(roots[i]);
+	}
+}
+
+/*
+ * Select one root from each contiguous per-socket enumeration group,
+ * then map every DF node to the root for its socket. The root socket ID
+ * cannot be read back, so the grouping follows PCI enumeration order.
+ *
+ * hygon_smn_reserved_roots owns the PCI references and config regions;
+ * hygon_smn_roots contains per-node aliases. Enable SMN access only after
+ * both arrays are complete.
+ */
+static int __init hygon_smn_setup(void)
+{
+	struct pci_dev **reserved_roots, **roots;
+	struct pci_dev *root = NULL;
+	u16 num_roots, roots_per_socket, node, num_nodes;
+	u16 num_sockets, reserved;
+	u8 socket_id;
+	int ret;
+
+	num_roots = 0;
+	while ((root = hygon_get_next_root(root)))
+		num_roots++;
+
+	pr_debug("Found %u Hygon SMN root devices\n", num_roots);
+
+	if (!num_roots)
+		return -ENODEV;
+
+	num_nodes = hygon_node_num();
+	if (!num_nodes)
+		return -ENODEV;
+
+	num_sockets = hygon_socket_num();
+	if (!num_sockets)
+		return -ENODEV;
+
+	if (num_roots % num_sockets) {
+		pr_err("Root count %u not divisible by socket count %u\n",
+		       num_roots, num_sockets);
+		return -ENODEV;
+	}
+
+	roots = kcalloc(num_nodes, sizeof(*roots), GFP_KERNEL);
+	if (!roots)
+		return -ENOMEM;
+
+	reserved_roots = kcalloc(num_roots, sizeof(*reserved_roots),
+				 GFP_KERNEL);
+	if (!reserved_roots) {
+		kfree(roots);
+		return -ENOMEM;
+	}
+
+	/* Reserve every root before publishing the node-to-root mapping. */
+	roots_per_socket = num_roots / num_sockets;
+	reserved = 0;
+	while ((root = hygon_get_next_root(root))) {
+		if (reserved >= num_roots) {
+			ret = -ENODEV;
+			goto err_release;
+		}
+
+		pci_dbg(root, "Reserving PCI config space\n");
+
+		/*
+		 * Mark the entire PCI config space kernel-exclusive because it
+		 * contains the SMN index/data registers.
+		 */
+		if (!pci_request_config_region_exclusive(root, 0,
+							 PCI_CFG_SPACE_SIZE,
+							 NULL)) {
+			pci_err(root, "Failed to reserve config space\n");
+			ret = -EEXIST;
+			goto err_release;
+		}
+
+		reserved_roots[reserved++] = pci_dev_get(root);
+	}
+
+	if (reserved != num_roots) {
+		pr_err("Root enumeration changed: expected %u roots, got %u\n",
+		       num_roots, reserved);
+		ret = -ENODEV;
+		goto err_release;
+	}
+
+	for (node = 0; node < num_nodes; node++) {
+		socket_id = hygon_node_socket(node);
+
+		if (socket_id >= num_sockets) {
+			ret = -ENODEV;
+			goto err_release;
+		}
+
+		roots[node] = reserved_roots[socket_id * roots_per_socket];
+
+		pci_dbg(roots[node],
+			"is root for Hygon node %u (socket %u)\n",
+			node, socket_id);
+	}
+
+	hygon_smn_reserved_roots = reserved_roots;
+	hygon_smn_roots = roots;
+	hygon_smn_num_nodes = num_nodes;
+	hygon_smn_exclusive = true;
+	return 0;
+
+err_release:
+	pci_dev_put(root);
+	hygon_release_reserved_roots(reserved_roots, reserved);
+	kfree(reserved_roots);
+	kfree(roots);
+	return ret;
+}
+
+/*
+ * Serialize the PCI index/data pair between in-kernel SMN users.
+ * The access sequence follows amd_smn_read() and amd_smn_write().
+ * hygon_smn_setup() provides the Hygon node-to-root mapping.
+ */
+static int __hygon_smn_rw(u16 node, u32 address, u32 *value, bool write)
+{
+	struct pci_dev *root;
+	int err;
+
+	if (!hygon_smn_exclusive || node >= hygon_smn_num_nodes)
+		return -ENODEV;
+
+	root = hygon_smn_roots[node];
+	if (!root)
+		return -ENODEV;
+
+	guard(mutex)(&hygon_smn_mutex);
+
+	err = pci_write_config_dword(root, HYGON_SMN_INDEX_OFFSET, address);
+	if (err) {
+		pr_warn("SMN index write failed (addr 0x%x)\n", address);
+		return pcibios_err_to_errno(err);
+	}
+
+	err = write ? pci_write_config_dword(root, HYGON_SMN_DATA_OFFSET, *value)
+		    : pci_read_config_dword(root, HYGON_SMN_DATA_OFFSET, value);
+
+	return pcibios_err_to_errno(err);
+}
+
+int hygon_smn_read(u16 node, u32 address, u32 *value)
+{
+	int err;
+
+	if (!value)
+		return -EINVAL;
+
+	err = __hygon_smn_rw(node, address, value, false);
+
+	/* Clear the output so callers do not consume a stale value. */
+	if (err) {
+		*value = 0;
+		return err;
+	}
+
+	/* Treat the PCI all-ones value as a missing device. */
+	if (PCI_POSSIBLE_ERROR(*value)) {
+		*value = 0;
+		return -ENODEV;
+	}
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(hygon_smn_read);
+
+int hygon_smn_write(u16 node, u32 address, u32 value)
+{
+	return __hygon_smn_rw(node, address, &value, true);
+}
+EXPORT_SYMBOL_GPL(hygon_smn_write);
+
 static int __init hygon_node_init(void)
 {
 	int ret;
@@ -731,6 +971,10 @@ static int __init hygon_node_init(void)
 		hygon_cache.num_nodes - hygon_cache.num_cdd,
 		hygon_cache.num_sockets);
 
+	ret = hygon_smn_setup();
+	if (ret)
+		pr_warn("SMN setup failed: %d\n", ret);
+
 	return 0;
 }
 fs_initcall(hygon_node_init);
-- 
2.43.0


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

* Re: [PATCH v5 2/8] x86/hygon: Map CPU NodeIds to DF nodes
  2026-09-10  8:27 ` [PATCH v5 2/8] x86/hygon: Map CPU NodeIds to DF nodes Lin Wang
@ 2026-09-10  8:32   ` sashiko-bot
  0 siblings, 0 replies; 19+ messages in thread
From: sashiko-bot @ 2026-09-10  8:32 UTC (permalink / raw)
  To: Lin Wang; +Cc: linux-hwmon

> On bare-metal Hygon systems, CPUID 0x8000001E ECX[7:0] encodes a
> CPU's socket and local CDD ordinal. The value is sparse across sockets
> and cannot be used as an index into the dense DF node cache.
> 
> Translate the CPUID value through the CDDs sorted by socket and DFID.
> The NodeId reported by CPUID in a guest may not describe the physical
> DF topology. Return -ENODEV in guests or when the NodeId does not
> identify an enumerated CDD.
> 
> Signed-off-by: Lin Wang <wanglin@open-hieco.net>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260910082734.3771675-1-wanglin@open-hieco.net?part=2


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

* Re: [PATCH v5 1/8] x86/hygon: Add Family 0x18 DF node enumeration
  2026-09-10  8:27 ` [PATCH v5 1/8] x86/hygon: Add Family 0x18 DF node enumeration Lin Wang
@ 2026-09-10  8:32   ` sashiko-bot
  0 siblings, 0 replies; 19+ messages in thread
From: sashiko-bot @ 2026-09-10  8:32 UTC (permalink / raw)
  To: Lin Wang; +Cc: linux-hwmon

> The AMD node code locates function 3 for node N at PCI slot 0x18 + N.
> Hygon has no equivalent node-index-to-slot mapping: DF F3 devices are
> discovered by PCI ID and the PCI BDF is only an access point.
> 
> F1x200 supplies the socket ID and SocketPresent mask. The DFID comes
> from F1x200 or F5x180, depending on the CPU model. The (socket ID,
> DFID) pair identifies a DF node. The cache is published only when the
> socket IDs observed during PCI enumeration match SocketPresent.
> 
> The resulting cache places Core and DDR Dies (CDDs) before I/O Dies
> (IODs), with each group ordered by socket and DFID. The node interfaces
> provide node counts and identity, F3/F4 lookup, and PCI device-to-node
> lookup.
> 
> Signed-off-by: Lin Wang <wanglin@open-hieco.net>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260910082734.3771675-1-wanglin@open-hieco.net?part=1


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

* Re: [PATCH v5 3/8] EDAC/amd64: Stop matching Hygon CPUs
  2026-09-10  8:27 ` [PATCH v5 3/8] EDAC/amd64: Stop matching Hygon CPUs Lin Wang
@ 2026-09-10  8:33   ` sashiko-bot
  0 siblings, 0 replies; 19+ messages in thread
From: sashiko-bot @ 2026-09-10  8:33 UTC (permalink / raw)
  To: Lin Wang; +Cc: linux-hwmon

> amd64_edac matches Hygon Family 0x18, but obtains its node count and
> F3 devices from the AMD northbridge cache. That cache maps dense node
> indices to fixed PCI slots starting at 0x18. Hygon discovers DF devices
> by PCI ID and reads their identities from DF registers. Their PCI slots
> do not encode node identity, and CPU NodeIds are sparse across sockets.
> The cache therefore cannot represent the Hygon topology.
> 
> A failed fixed-slot F3 lookup causes amd_cache_northbridges() to discard
> the cache, and amd64_edac then exits at its amd_nb_num() check. The
> driver has no valid Hygon node count or node-to-F3 mapping.
> 
> Remove the Hygon CPU match and the unused Family 0x18 case. Hygon memory
> controller support requires the Hygon node interfaces and will be added
> separately. This does not affect MCE decoding in mce_amd.c.
> 
> Signed-off-by: Lin Wang <wanglin@open-hieco.net>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260910082734.3771675-1-wanglin@open-hieco.net?part=3


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

* Re: [PATCH v5 4/8] RAS/AMD/ATL: Match AMD CPUs only
  2026-09-10  8:27 ` [PATCH v5 4/8] RAS/AMD/ATL: Match AMD CPUs only Lin Wang
@ 2026-09-10  8:34   ` sashiko-bot
  0 siblings, 0 replies; 19+ messages in thread
From: sashiko-bot @ 2026-09-10  8:34 UTC (permalink / raw)
  To: Lin Wang; +Cc: linux-hwmon

> The ATL CPU table matches Hygon through X86_FEATURE_SMCA and
> X86_FEATURE_ZEN, but the library obtains its node count and F4 devices
> from the AMD northbridge cache. That cache maps dense node indices to
> fixed PCI slots. Hygon PCI slots do not encode node identity, and CPU
> NodeIds are sparse across sockets. The cache therefore cannot represent
> the Hygon topology or provide ATL with a valid node-to-F4 mapping.
> 
> A failed fixed-slot F3 lookup leaves the cache empty, so amd_atl_init()
> exits at its amd_nb_num() check.
> 
> Limit both feature matches to AMD. Hygon address translation requires
> the Hygon node interfaces and will be added separately.
> 
> Signed-off-by: Lin Wang <wanglin@open-hieco.net>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260910082734.3771675-1-wanglin@open-hieco.net?part=4


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

* Re: [PATCH v5 5/8] hwmon: (k10temp) Stop matching Hygon devices
  2026-09-10  8:27 ` [PATCH v5 5/8] hwmon: (k10temp) Stop matching Hygon devices Lin Wang
@ 2026-09-10  8:36   ` sashiko-bot
  2026-09-11  0:52   ` Guenter Roeck
  1 sibling, 0 replies; 19+ messages in thread
From: sashiko-bot @ 2026-09-10  8:36 UTC (permalink / raw)
  To: Lin Wang; +Cc: linux-hwmon

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [Low] Incomplete removal of Hygon support in documentation
--

commit 5233d0bb0c626dfef82d25286284d5ba59e76f21
Author: Lin Wang <wanglin@open-hieco.net>

hwmon: (k10temp) Stop matching Hygon devices

This commit removes the Hygon PCI match and the Family 0x18 branch from the
k10temp driver. Hygon temperature monitoring will be added separately, as
the derived node number currently has no defined relationship to the Hygon
DF node or its SMN root.

> diff --git a/drivers/hwmon/k10temp.c b/drivers/hwmon/k10temp.c
> index 75a45010d687a..20a5470e6593f 100644
> --- a/drivers/hwmon/k10temp.c
> +++ b/drivers/hwmon/k10temp.c
> @@ -475,7 +475,7 @@ static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id)
>  		data->read_tempreg = read_tempreg_pci;
>  	}
>  
> -	if (boot_cpu_data.x86 == 0x17 || boot_cpu_data.x86 == 0x18) {
> +	if (boot_cpu_data.x86 == 0x17) {

[Severity: Low]
Does Documentation/hwmon/k10temp.rst need to be updated to match this code
removal?

Currently, the documentation file incorrectly lists the chip as supported:

  * AMD Family 18h processors: "Hygon Dhyana"

If a user consults Documentation/hwmon/k10temp.rst for driver compatibility,
could this regression mislead them into thinking the k10temp driver still
supports it?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260910082734.3771675-1-wanglin@open-hieco.net?part=5

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

* Re: [PATCH v5 6/8] x86/amd_nb: Restrict the northbridge framework to AMD CPUs
  2026-09-10  8:27 ` [PATCH v5 6/8] x86/amd_nb: Restrict the northbridge framework to AMD CPUs Lin Wang
@ 2026-09-10  8:37   ` sashiko-bot
  0 siblings, 0 replies; 19+ messages in thread
From: sashiko-bot @ 2026-09-10  8:37 UTC (permalink / raw)
  To: Lin Wang; +Cc: linux-hwmon

> amd_cache_northbridges() obtains function 3 and function 4 for node N
> from fixed PCI slot 0x18 + N. Hygon DF devices are discovered by PCI
> ID and their slots do not encode node identity, so this cache cannot
> represent the Hygon topology.
> 
> The preceding patches remove the Hygon matches from amd64_edac and ATL.
> Restrict the northbridge initialization and early device check to AMD
> CPUs. Keep amd_get_mmconfig_range() available on Hygon because it reads
> the MMCONFIG MSR without using the northbridge cache.
> 
> Reviewed-by: Yazen Ghannam <yazen.ghannam@amd.com>
> Signed-off-by: Lin Wang <wanglin@open-hieco.net>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260910082734.3771675-1-wanglin@open-hieco.net?part=6


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

* Re: [PATCH v5 8/8] x86/hygon: Add Family 0x18 SMN access
  2026-09-10  8:27 ` [PATCH v5 8/8] x86/hygon: Add Family 0x18 SMN access Lin Wang
@ 2026-09-10  8:38   ` sashiko-bot
  0 siblings, 0 replies; 19+ messages in thread
From: sashiko-bot @ 2026-09-10  8:38 UTC (permalink / raw)
  To: Lin Wang; +Cc: linux-hwmon

> Hygon Family 0x18 DF nodes in a socket share an SMN ingress. The root
> groups therefore need to be associated with sockets before they can be
> expanded to the dense DF node indices.
> 
> Build the node-to-root map after DF enumeration and expose Hygon SMN
> read and write helpers. Hold a reference and reserve PCI config space
> for every root used by the built-in node layer. SMN remains unavailable
> if root discovery or mapping fails.
> 
> Signed-off-by: Lin Wang <wanglin@open-hieco.net>

Sashiko has reviewed this patch and found no issues. It looks great!

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260910082734.3771675-1-wanglin@open-hieco.net?part=8


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

* Re: [PATCH v5 7/8] x86/amd_node: Restrict SMN setup to AMD CPUs
  2026-09-10  8:27 ` [PATCH v5 7/8] x86/amd_node: Restrict SMN setup " Lin Wang
@ 2026-09-10  8:43   ` sashiko-bot
  0 siblings, 0 replies; 19+ messages in thread
From: sashiko-bot @ 2026-09-10  8:43 UTC (permalink / raw)
  To: Lin Wang; +Cc: linux-hwmon

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] The `amd_roots` array stores dangling pointers to PCI devices because their reference counts are incorrectly dropped during the iteration loop.
--

commit 263066910679f0796c85e4dd7d844bda35fa7403
Author: Lin Wang <wanglin@open-hieco.net>

x86/amd_node: Restrict SMN setup to AMD CPUs

amd_smn_init() divides host bridge roots among amd_num_nodes() entries
in PCI enumeration order. This restricts AMD SMN setup and root discovery
to AMD CPUs.

> diff --git a/arch/x86/kernel/amd_node.c b/arch/x86/kernel/amd_node.c
> index 762585775b5a2..a7190cec69954 100644
> --- a/arch/x86/kernel/amd_node.c
> +++ b/arch/x86/kernel/amd_node.c
> @@ -229,8 +229,7 @@ static struct pci_dev *get_next_root(struct pci_dev *root)
>  		if (root->devfn)
>  			continue;
>  
> -		if (root->vendor != PCI_VENDOR_ID_AMD &&
> -		    root->vendor != PCI_VENDOR_ID_HYGON)
> +		if (root->vendor != PCI_VENDOR_ID_AMD)
>  			continue;
>  
>  		break;

[Severity: High]
This isn't a bug introduced by this patch, but does the amd_roots array
store dangling pointers to PCI devices because their reference counts are
incorrectly dropped during the iteration loop?

This function delegates to pci_get_class(), which by design drops the
reference count of the device passed to it. In the second loop of
amd_smn_init(), a newly matched root device is stored into amd_roots
without acquiring an additional reference:

arch/x86/kernel/amd_node.c:amd_smn_init() {
    ...
	while (node < num_nodes && (root = get_next_root(root))) {
		/* Use one root for each node and skip the rest. */
		if (count++ % roots_per_node)
			continue;

		pci_dbg(root, "is root for AMD node %u\n", node);
		amd_roots[node++] = root;
	}
    ...
}

In the next iteration, the loop condition root = get_next_root(root) passes
that same root back to pci_get_class(), immediately decrementing its
reference count. This leaves the amd_roots array populated with dangling
pointers.

Conversely, the reference count of the very last device retrieved is leaked
because the loop terminates when node < num_nodes fails before it can be
passed back into get_next_root().

While host bridges are typically not hot-removable in this architecture, could
this be a structural use-after-free and reference leak?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260910082734.3771675-1-wanglin@open-hieco.net?part=7

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

* Re: [PATCH v5 5/8] hwmon: (k10temp) Stop matching Hygon devices
  2026-09-10  8:27 ` [PATCH v5 5/8] hwmon: (k10temp) Stop matching Hygon devices Lin Wang
  2026-09-10  8:36   ` sashiko-bot
@ 2026-09-11  0:52   ` Guenter Roeck
  2026-09-11  1:10     ` Lin Wang
  1 sibling, 1 reply; 19+ messages in thread
From: Guenter Roeck @ 2026-09-11  0:52 UTC (permalink / raw)
  To: Lin Wang
  Cc: Borislav Petkov, Yazen Ghannam, Mario Limonciello, Pu Wen,
	Thomas Gleixner, Ingo Molnar, Dave Hansen, H. Peter Anvin,
	Tony Luck, Clemens Ladisch, x86, linux-edac, linux-hwmon,
	linux-kernel

On Thu, Sep 10, 2026 at 04:27:31PM +0800, Lin Wang wrote:
> k10temp matches the Hygon F3 device and uses the AMD SMN accessors.
> These accessors derive an SMN node number by subtracting PCI slot 0x18
> from the F3 slot, then pass the result to amd_smn_read(). Hygon PCI
> slots do not encode DF node identity, and their SMN roots are grouped
> by socket.
> 
> The derived node number therefore has no defined relationship to the
> Hygon DF node or its SMN root.
> 
> Remove the Hygon PCI match and Family 0x18 branch. Hygon temperature
> monitoring requires the Hygon node and SMN interfaces and will be added
> separately.
> 
> Signed-off-by: Lin Wang <wanglin@open-hieco.net>

As reported by Sasjiko, the documentation should be updated as well.

Thanks,
Guenter

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

* Re: [PATCH v5 5/8] hwmon: (k10temp) Stop matching Hygon devices
  2026-09-11  0:52   ` Guenter Roeck
@ 2026-09-11  1:10     ` Lin Wang
  0 siblings, 0 replies; 19+ messages in thread
From: Lin Wang @ 2026-09-11  1:10 UTC (permalink / raw)
  To: Guenter Roeck
  Cc: Borislav Petkov, Yazen Ghannam, Mario Limonciello, Pu Wen,
	Thomas Gleixner, Ingo Molnar, Dave Hansen, H. Peter Anvin,
	Tony Luck, Clemens Ladisch, x86, linux-edac, linux-hwmon,
	linux-kernel

On 9/11/2026 8:52 AM, Guenter Roeck wrote:
>> Remove the Hygon PCI match and Family 0x18 branch. Hygon temperature
>> monitoring requires the Hygon node and SMN interfaces and will be added
>> separately.
>>
>> Signed-off-by: Lin Wang<wanglin@open-hieco.net>
> As reported by Sasjiko, the documentation should be updated as well.
> 
> Thanks,
> Guenter

Yes. I will remove the Hygon Dhyana entry and update the Family 18h
temperature sensor description in the next revision.

Thanks & Br,

Lin

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

end of thread, other threads:[~2026-09-11  1:11 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-10  8:27 [PATCH v5 0/8] x86/hygon: Add Family 0x18 DF node enumeration and SMN access Lin Wang
2026-09-10  8:27 ` [PATCH v5 1/8] x86/hygon: Add Family 0x18 DF node enumeration Lin Wang
2026-09-10  8:32   ` sashiko-bot
2026-09-10  8:27 ` [PATCH v5 2/8] x86/hygon: Map CPU NodeIds to DF nodes Lin Wang
2026-09-10  8:32   ` sashiko-bot
2026-09-10  8:27 ` [PATCH v5 3/8] EDAC/amd64: Stop matching Hygon CPUs Lin Wang
2026-09-10  8:33   ` sashiko-bot
2026-09-10  8:27 ` [PATCH v5 4/8] RAS/AMD/ATL: Match AMD CPUs only Lin Wang
2026-09-10  8:34   ` sashiko-bot
2026-09-10  8:27 ` [PATCH v5 5/8] hwmon: (k10temp) Stop matching Hygon devices Lin Wang
2026-09-10  8:36   ` sashiko-bot
2026-09-11  0:52   ` Guenter Roeck
2026-09-11  1:10     ` Lin Wang
2026-09-10  8:27 ` [PATCH v5 6/8] x86/amd_nb: Restrict the northbridge framework to AMD CPUs Lin Wang
2026-09-10  8:37   ` sashiko-bot
2026-09-10  8:27 ` [PATCH v5 7/8] x86/amd_node: Restrict SMN setup " Lin Wang
2026-09-10  8:43   ` sashiko-bot
2026-09-10  8:27 ` [PATCH v5 8/8] x86/hygon: Add Family 0x18 SMN access Lin Wang
2026-09-10  8:38   ` sashiko-bot

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