From e7332c14bed10f6b7de92a8eb367872286827793 Mon Sep 17 00:00:00 2001 From: Wenhui Fan Date: Thu, 14 May 2026 11:21:40 +0800 Subject: [PATCH 01/12] anolis:x86/pci, x86/hygon_nb: Add Hygon Northbridge driver support via PCI enumeration ANBZ: #35240 Separate Hygon NB drivers from AMD NB drivers The key changes as follows: 1. Implement Hygon NB driver by PCI device enumeration. 2. Add Hygon NB driver source file. 3. Add Kconfig entry (CONFIG_HYGON_NB) with dependencies on AMD_NB and CPU_SUP_HYGON. Hygon-SIG: commit none hygon anolis:x86/pci, x86/hygon_nb: Add Hygon Northbridge driver support via PCI enumeration Signed-off-by: Wenhui Fan --- .../configs/L1-RECOMMEND/x86/CONFIG_HYGON_NB | 1 + arch/x86/Kconfig | 4 + arch/x86/include/asm/hygon/hygon_nb.h | 34 +++ arch/x86/kernel/Makefile | 1 + arch/x86/kernel/amd_nb.c | 288 +----------------- arch/x86/kernel/hygon_nb.c | 137 +++++++++ include/linux/pci_ids.h | 1 + 7 files changed, 179 insertions(+), 287 deletions(-) create mode 100644 anolis/configs/L1-RECOMMEND/x86/CONFIG_HYGON_NB create mode 100644 arch/x86/include/asm/hygon/hygon_nb.h create mode 100644 arch/x86/kernel/hygon_nb.c diff --git a/anolis/configs/L1-RECOMMEND/x86/CONFIG_HYGON_NB b/anolis/configs/L1-RECOMMEND/x86/CONFIG_HYGON_NB new file mode 100644 index 000000000000..290c38414c64 --- /dev/null +++ b/anolis/configs/L1-RECOMMEND/x86/CONFIG_HYGON_NB @@ -0,0 +1 @@ +CONFIG_HYGON_NB=y diff --git a/arch/x86/Kconfig b/arch/x86/Kconfig index f22b398dd04f..848820cc8e0d 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -3111,6 +3111,10 @@ config AMD_NODE def_bool y depends on CPU_SUP_AMD && PCI +config HYGON_NB + def_bool y + depends on CPU_SUP_HYGON && PCI + endmenu menu "Binary Emulations" diff --git a/arch/x86/include/asm/hygon/hygon_nb.h b/arch/x86/include/asm/hygon/hygon_nb.h new file mode 100644 index 000000000000..f93efad33d3b --- /dev/null +++ b/arch/x86/include/asm/hygon/hygon_nb.h @@ -0,0 +1,34 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _ASM_X86_HYGON_NB_H +#define _ASM_X86_HYGON_NB_H + +#include + +struct hygon_northbridge { + struct pci_dev *root; + struct pci_dev *misc; + struct pci_dev *link; +}; + +struct hygon_northbridge_info { + u16 num; + struct hygon_northbridge *nb; +}; + +#ifdef CONFIG_HYGON_NB + +int northbridge_init_hygon(void); +u16 hygon_nb_num(void); +struct hygon_northbridge *node_to_hygon_nb(int node); + +#else + +#define northbridge_init_hygon(x) 0 +#define hygon_nb_num(x) 0 +static inline struct hygon_northbridge *node_to_hygon_nb(int node) +{ + return NULL; +} +#endif + +#endif diff --git a/arch/x86/kernel/Makefile b/arch/x86/kernel/Makefile index 9ff1490bdd4a..e3311bd0a089 100644 --- a/arch/x86/kernel/Makefile +++ b/arch/x86/kernel/Makefile @@ -114,6 +114,7 @@ obj-$(CONFIG_HPET_TIMER) += hpet.o obj-$(CONFIG_AMD_NB) += amd_nb.o obj-$(CONFIG_AMD_NODE) += amd_node.o +obj-$(CONFIG_HYGON_NB) += hygon_nb.o obj-$(CONFIG_DEBUG_NMI_SELFTEST) += nmi_selftest.o obj-$(CONFIG_KVM_GUEST) += kvm.o kvmclock.o diff --git a/arch/x86/kernel/amd_nb.c b/arch/x86/kernel/amd_nb.c index 1d9b2e5047cb..5a0631789742 100644 --- a/arch/x86/kernel/amd_nb.c +++ b/arch/x86/kernel/amd_nb.c @@ -21,18 +21,7 @@ #define PCI_DEVICE_ID_AMD_17H_DF_F4 0x1464 #define PCI_DEVICE_ID_AMD_17H_M30H_DF_F4 0x1494 -#define PCI_DEVICE_ID_HYGON_18H_M05H_ROOT 0x14a0 -#define PCI_DEVICE_ID_HYGON_18H_M10H_ROOT 0x14c0 -#define PCI_DEVICE_ID_HYGON_18H_M18H_ROOT 0x2000 -#define PCI_DEVICE_ID_HYGON_18H_M04H_DF_F1 0x1491 -#define PCI_DEVICE_ID_HYGON_18H_M05H_DF_F1 0x14b1 -#define PCI_DEVICE_ID_HYGON_18H_M05H_DF_F4 0x14b4 -#define PCI_DEVICE_ID_HYGON_18H_M10H_DF_F4 0x14d4 -#define PCI_DEVICE_ID_HYGON_18H_M18H_DF_F4 0x2014 -#define PCI_DEVICE_ID_HYGON_18H_M06H_DF_F5 0x14b5 - static u32 *flush_words; -static u16 nb_num; static const struct pci_device_id amd_nb_misc_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_K8_NB_MISC) }, @@ -46,33 +35,6 @@ static const struct pci_device_id amd_nb_misc_ids[] = { {} }; -static const struct pci_device_id hygon_root_ids[] = { - { PCI_DEVICE(PCI_VENDOR_ID_HYGON, PCI_DEVICE_ID_AMD_17H_ROOT) }, - { PCI_DEVICE(PCI_VENDOR_ID_HYGON, PCI_DEVICE_ID_AMD_17H_M30H_ROOT) }, - { PCI_DEVICE(PCI_VENDOR_ID_HYGON, PCI_DEVICE_ID_HYGON_18H_M05H_ROOT) }, - { PCI_DEVICE(PCI_VENDOR_ID_HYGON, PCI_DEVICE_ID_HYGON_18H_M10H_ROOT) }, - { PCI_DEVICE(PCI_VENDOR_ID_HYGON, PCI_DEVICE_ID_HYGON_18H_M18H_ROOT) }, - {} -}; - -static const struct pci_device_id hygon_nb_misc_ids[] = { - { PCI_DEVICE(PCI_VENDOR_ID_HYGON, PCI_DEVICE_ID_AMD_17H_DF_F3) }, - { PCI_DEVICE(PCI_VENDOR_ID_HYGON, PCI_DEVICE_ID_AMD_17H_M30H_DF_F3) }, - { PCI_DEVICE(PCI_VENDOR_ID_HYGON, PCI_DEVICE_ID_HYGON_18H_M05H_DF_F3) }, - { PCI_DEVICE(PCI_VENDOR_ID_HYGON, PCI_DEVICE_ID_HYGON_18H_M10H_DF_F3) }, - { PCI_DEVICE(PCI_VENDOR_ID_HYGON, PCI_DEVICE_ID_HYGON_18H_M18H_DF_F3) }, - {} -}; - -static const struct pci_device_id hygon_nb_link_ids[] = { - { PCI_DEVICE(PCI_VENDOR_ID_HYGON, PCI_DEVICE_ID_AMD_17H_DF_F4) }, - { PCI_DEVICE(PCI_VENDOR_ID_HYGON, PCI_DEVICE_ID_AMD_17H_M30H_DF_F4) }, - { PCI_DEVICE(PCI_VENDOR_ID_HYGON, PCI_DEVICE_ID_HYGON_18H_M05H_DF_F4) }, - { PCI_DEVICE(PCI_VENDOR_ID_HYGON, PCI_DEVICE_ID_HYGON_18H_M10H_DF_F4) }, - { PCI_DEVICE(PCI_VENDOR_ID_HYGON, PCI_DEVICE_ID_HYGON_18H_M18H_DF_F4) }, - {} -}; - const struct amd_nb_bus_dev_range amd_nb_bus_dev_ranges[] __initconst = { { 0x00, 0x18, 0x20 }, { 0xff, 0x00, 0x20 }, @@ -111,243 +73,6 @@ static struct pci_dev *next_northbridge(struct pci_dev *dev, return dev; } -bool hygon_f18h_m4h(void) -{ - if (boot_cpu_data.x86_vendor != X86_VENDOR_HYGON) - return false; - - if (boot_cpu_data.x86 == 0x18 && - boot_cpu_data.x86_model >= 0x4 && - boot_cpu_data.x86_model <= 0xf) - return true; - - return false; -} -EXPORT_SYMBOL_GPL(hygon_f18h_m4h); - -bool hygon_f18h_m10h(void) -{ - if (boot_cpu_data.x86_vendor != X86_VENDOR_HYGON) - return false; - - if (boot_cpu_data.x86 == 0x18 && - boot_cpu_data.x86_model >= 0x10 && - boot_cpu_data.x86_model <= 0x1f) - return true; - - return false; -} -EXPORT_SYMBOL_GPL(hygon_f18h_m10h); - -u16 hygon_nb_num(void) -{ - return nb_num; -} -EXPORT_SYMBOL_GPL(hygon_nb_num); - -static int get_df_register(struct pci_dev *misc, u8 func, int offset, u32 *value) -{ - struct pci_dev *df_func = NULL; - u32 device; - int err; - - if (func == 1) { - switch (boot_cpu_data.x86_model) { - case 0x4: - device = PCI_DEVICE_ID_HYGON_18H_M04H_DF_F1; - break; - case 0x5: - if (misc->device == PCI_DEVICE_ID_HYGON_18H_M05H_DF_F3) - device = PCI_DEVICE_ID_HYGON_18H_M05H_DF_F1; - else - device = PCI_DEVICE_ID_HYGON_18H_M04H_DF_F1; - break; - case 0x6 ... 0x8: - device = PCI_DEVICE_ID_HYGON_18H_M05H_DF_F1; - break; - default: - return -ENODEV; - } - } else if (func == 5) { - switch (boot_cpu_data.x86_model) { - case 0x6 ... 0x8: - device = PCI_DEVICE_ID_HYGON_18H_M06H_DF_F5; - break; - default: - return -ENODEV; - } - } else { - return -ENODEV; - } - - while ((df_func = pci_get_device(misc->vendor, device, df_func))) - if (pci_domain_nr(df_func->bus) == pci_domain_nr(misc->bus) && - df_func->bus->number == misc->bus->number && - PCI_SLOT(df_func->devfn) == PCI_SLOT(misc->devfn)) - break; - - if (!df_func) { - pr_warn("Error getting DF F%d device.\n", func); - return -ENODEV; - } - - err = pci_read_config_dword(df_func, offset, value); - if (err) - pr_warn("Error reading DF F%d register.\n", func); - - return err; -} - -int get_df_id(struct pci_dev *misc, u8 *id) -{ - u32 value; - int ret; - - if (boot_cpu_data.x86_model >= 0x6 && - boot_cpu_data.x86_model <= 0xf) { - /* F5x180[19:16]: DF ID */ - ret = get_df_register(misc, 5, 0x180, &value); - *id = (value >> 16) & 0xf; - } else { - /* F1x200[23:20]: DF ID */ - ret = get_df_register(misc, 1, 0x200, &value); - *id = (value >> 20) & 0xf; - } - - return ret; -} -EXPORT_SYMBOL_GPL(get_df_id); - -static u8 get_socket_num(struct pci_dev *misc) -{ - u32 value; - int ret; - - /* F1x200[7:0]: Which socket is present. */ - ret = get_df_register(misc, 1, 0x200, &value); - - return ret ? 0 : hweight8(value & 0xff); -} - -static int northbridge_init_f18h_m4h(const struct pci_device_id *root_ids, - const struct pci_device_id *misc_ids, - const struct pci_device_id *link_ids) -{ - struct pci_dev *root, *misc, *link; - struct pci_dev *root_first = NULL; - struct amd_northbridge *nb; - u16 roots_per_socket = 0; - u16 miscs_per_socket = 0; - u16 socket_num = 0; - u16 root_count = 0; - u16 misc_count = 0; - int err = -ENODEV; - u8 i, j, m, n; - u8 id; - - pr_info("Hygon Fam%xh Model%xh NB driver.\n", - boot_cpu_data.x86, boot_cpu_data.x86_model); - - misc = next_northbridge(NULL, misc_ids); - if (misc != NULL) { - socket_num = get_socket_num(misc); - pr_info("Socket number: %d\n", socket_num); - if (!socket_num) { - err = -ENODEV; - goto ret; - } - } else { - err = -ENODEV; - goto ret; - } - - misc = NULL; - while ((misc = next_northbridge(misc, misc_ids)) != NULL) - misc_count++; - - root = NULL; - while ((root = next_northbridge(root, root_ids)) != NULL) - root_count++; - - if (!root_count || !misc_count) { - err = -ENODEV; - goto ret; - } - - /* - * There should be _exactly_ N roots for each DF/SMN - * interface, and M DF/SMN interfaces in one socket. - */ - roots_per_socket = root_count / socket_num; - miscs_per_socket = misc_count / socket_num; - - if (!roots_per_socket || !miscs_per_socket) { - err = -ENODEV; - goto ret; - } - - nb = kcalloc(misc_count, sizeof(struct amd_northbridge), GFP_KERNEL); - if (!nb) { - err = -ENOMEM; - goto ret; - } - - amd_northbridges.nb = nb; - amd_northbridges.num = misc_count; - - link = misc = root = NULL; - j = m = n = 0; - for (i = 0; i < amd_northbridges.num; i++) { - misc = next_northbridge(misc, misc_ids); - link = next_northbridge(link, link_ids); - - /* Only save the first PCI root device for each socket. */ - if (!(i % miscs_per_socket)) { - root_first = next_northbridge(root, root_ids); - root = root_first; - j = 1; - } - - if (get_df_id(misc, &id)) { - err = -ENODEV; - goto err; - } - pr_info("DF ID: %d\n", id); - - if (id < 4) { - /* Add the devices with id<4 from the tail. */ - node_to_amd_nb(misc_count - m - 1)->misc = misc; - node_to_amd_nb(misc_count - m - 1)->link = link; - node_to_amd_nb(misc_count - m - 1)->root = root_first; - m++; - } else { - node_to_amd_nb(n)->misc = misc; - node_to_amd_nb(n)->link = link; - node_to_amd_nb(n)->root = root_first; - n++; - } - - /* Skip the redundant PCI root devices per socket. */ - while (j < roots_per_socket) { - root = next_northbridge(root, root_ids); - j++; - } - } - nb_num = n; - - return 0; - -err: - kfree(nb); - amd_northbridges.nb = NULL; - -ret: - if (!boot_cpu_has(X86_FEATURE_HYPERVISOR)) - pr_err("Hygon Fam%xh Model%xh northbridge init failed(%d)!\n", - boot_cpu_data.x86, boot_cpu_data.x86_model, err); - return err; -} - static int amd_cache_northbridges(void) { struct amd_northbridge *nb; @@ -356,16 +81,6 @@ static int amd_cache_northbridges(void) if (amd_northbridges.num) return 0; - if (boot_cpu_data.x86_vendor == X86_VENDOR_HYGON && - boot_cpu_data.x86_model >= 0x4 && boot_cpu_data.x86_model <= 0xf) { - const struct pci_device_id *root_ids = hygon_root_ids; - const struct pci_device_id *misc_ids = hygon_nb_misc_ids; - const struct pci_device_id *link_ids = hygon_nb_link_ids; - - return northbridge_init_f18h_m4h(root_ids, - misc_ids, link_ids); - } - amd_northbridges.num = amd_num_nodes(); nb = kcalloc(amd_northbridges.num, sizeof(struct amd_northbridge), GFP_KERNEL); @@ -618,8 +333,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(); diff --git a/arch/x86/kernel/hygon_nb.c b/arch/x86/kernel/hygon_nb.c new file mode 100644 index 000000000000..c2dcffbd7ec2 --- /dev/null +++ b/arch/x86/kernel/hygon_nb.c @@ -0,0 +1,137 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Share support code for Hygon northbridges and derivatives. + * Copyright (C) 2026 Chengdu Haiguang IC Design Co., Ltd. + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include + +#define PCI_DEVICE_ID_HYGON_18H_ROOT 0x1450 + +#define PCI_DEVICE_ID_HYGON_18H_DF_F4 0x1464 + +static struct pci_dev **hygon_roots; +static struct hygon_northbridge_info hygon_northbridges; + +static const struct pci_device_id hygon_root_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_HYGON, PCI_DEVICE_ID_HYGON_18H_ROOT) }, + {} +}; + +static const struct pci_device_id hygon_nb_misc_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_HYGON, PCI_DEVICE_ID_HYGON_18H_DF_F3) }, + {} +}; + +static const struct pci_device_id hygon_nb_link_ids[] = { + { PCI_DEVICE(PCI_VENDOR_ID_HYGON, PCI_DEVICE_ID_HYGON_18H_DF_F4) }, + {} +}; + +static struct pci_dev *next_northbridge(struct pci_dev *dev, + const struct pci_device_id *ids) +{ + do { + dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev); + if (!dev) + break; + } while (!pci_match_id(ids, dev)); + return dev; +} + +u16 hygon_nb_num(void) +{ + return hygon_northbridges.num; +} +EXPORT_SYMBOL_GPL(hygon_nb_num); + +struct hygon_northbridge *node_to_hygon_nb(int node) +{ + return (node < hygon_northbridges.num) ? &hygon_northbridges.nb[node] : NULL; +} +EXPORT_SYMBOL_GPL(node_to_hygon_nb); + +int northbridge_init_hygon(void) +{ + const struct pci_device_id *misc_ids = hygon_nb_misc_ids; + const struct pci_device_id *link_ids = hygon_nb_link_ids; + const struct pci_device_id *root_ids = hygon_root_ids; + struct pci_dev *root, *misc, *link; + struct hygon_northbridge *nb; + u16 misc_count = 0; + int err = -ENODEV; + u16 i = 0; + + if (hygon_northbridges.num) + return 0; + + misc = NULL; + while ((misc = next_northbridge(misc, misc_ids))) + misc_count++; + + if (!misc_count) { + err = -ENODEV; + goto out; + } + + hygon_roots = kcalloc(misc_count, sizeof(*hygon_roots), GFP_KERNEL); + if (!hygon_roots) { + err = -ENOMEM; + goto out; + } + + nb = kcalloc(misc_count, sizeof(struct hygon_northbridge), GFP_KERNEL); + if (!nb) { + err = -ENOMEM; + goto err_free_roots; + } + + hygon_northbridges.nb = nb; + hygon_northbridges.num = misc_count; + + link = misc = root = NULL; + for (i = 0; i < hygon_northbridges.num; i++) { + hygon_roots[i] = root = + next_northbridge(root, root_ids); + node_to_hygon_nb(i)->misc = misc = + next_northbridge(misc, misc_ids); + node_to_hygon_nb(i)->link = link = + next_northbridge(link, link_ids); + } + + pr_info("Hygon Fam%xh Model%xh NB driver init success.\n", + boot_cpu_data.x86, boot_cpu_data.x86_model); + + return 0; + +err_free_roots: + kfree(hygon_roots); + +out: + if (!boot_cpu_has(X86_FEATURE_HYPERVISOR)) + pr_err("Hygon Fam%xh Model%xh northbridge init failed(%d)!\n", + boot_cpu_data.x86, boot_cpu_data.x86_model, err); + return err; +} + +static __init int init_hygon_nbs(void) +{ + if (boot_cpu_data.x86_vendor != X86_VENDOR_HYGON) + return 0; + + northbridge_init_hygon(); + + return 0; +} + +/* This has to go after the PCI subsystem */ +fs_initcall(init_hygon_nbs); diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 2dc035374f79..ed7c9f85427c 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2604,6 +2604,7 @@ #define PCI_DEVICE_ID_HYGON_18H_M05H_HDA 0x14a9 #define PCI_DEVICE_ID_HYGON_18H_M10H_HDA 0x14c9 #define PCI_DEVICE_ID_HYGON_18H_M18H_HDA 0x2007 +#define PCI_DEVICE_ID_HYGON_18H_DF_F3 0x1463 #define PCI_DEVICE_ID_HYGON_18H_M05H_DF_F3 0x14b3 #define PCI_DEVICE_ID_HYGON_18H_M10H_DF_F3 0x14d3 #define PCI_DEVICE_ID_HYGON_18H_M18H_DF_F3 0x2013 -- Gitee From 8358fad6b138a5ce937df86f9720518b5c64ac05 Mon Sep 17 00:00:00 2001 From: Wenhui Fan Date: Thu, 14 May 2026 11:27:39 +0800 Subject: [PATCH 02/12] anolis:x86/hygon_nb: Add hygon smn access path ANBZ: #35240 SMN accesses are done indirectly through an index/data pair in PCI config space for Hygon processor. Hygon-SIG: commit none hygon anolis:x86/hygon_nb: Add hygon smn access path Signed-off-by: Wenhui Fan --- arch/x86/include/asm/hygon/hygon_nb.h | 3 ++ arch/x86/kernel/hygon_nb.c | 57 +++++++++++++++++++++++++++ 2 files changed, 60 insertions(+) diff --git a/arch/x86/include/asm/hygon/hygon_nb.h b/arch/x86/include/asm/hygon/hygon_nb.h index f93efad33d3b..f5d1e65b8184 100644 --- a/arch/x86/include/asm/hygon/hygon_nb.h +++ b/arch/x86/include/asm/hygon/hygon_nb.h @@ -15,6 +15,9 @@ struct hygon_northbridge_info { struct hygon_northbridge *nb; }; +int hygon_smn_read(u16 node, u32 address, u32 *value); +int hygon_smn_write(u16 node, u32 address, u32 value); + #ifdef CONFIG_HYGON_NB int northbridge_init_hygon(void); diff --git a/arch/x86/kernel/hygon_nb.c b/arch/x86/kernel/hygon_nb.c index c2dcffbd7ec2..69ab322cf8d5 100644 --- a/arch/x86/kernel/hygon_nb.c +++ b/arch/x86/kernel/hygon_nb.c @@ -22,6 +22,9 @@ static struct pci_dev **hygon_roots; static struct hygon_northbridge_info hygon_northbridges; +/* Protect the PCI config register pairs used for SMN and DF indirect access. */ +static DEFINE_MUTEX(smn_mutex); + static const struct pci_device_id hygon_root_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_HYGON, PCI_DEVICE_ID_HYGON_18H_ROOT) }, {} @@ -37,6 +40,60 @@ static const struct pci_device_id hygon_nb_link_ids[] = { {} }; +#define HYGON_SMN_INDEX_OFFSET 0x60 +#define HYGON_SMN_DATA_OFFSET 0x64 + +static int __hygon_smn_rw(u16 node, u32 address, u32 *value, bool write) +{ + struct pci_dev *root; + int err = -ENODEV; + + if (node >= hygon_nb_num()) + goto out; + + root = hygon_roots[node]; + if (!root) + goto out; + + mutex_lock(&smn_mutex); + + err = pci_write_config_dword(root, HYGON_SMN_INDEX_OFFSET, address); + if (err) { + pr_warn("Error programming SMN address 0x%x.\n", address); + goto out_unlock; + } + + err = (write ? pci_write_config_dword(root, HYGON_SMN_DATA_OFFSET, *value) + : pci_read_config_dword(root, HYGON_SMN_DATA_OFFSET, value)); + if (err) + pr_warn("Error %s SMN address 0x%x.\n", + (write ? "writing to" : "reading from"), address); + +out_unlock: + mutex_unlock(&smn_mutex); + +out: + return err; +} + +int hygon_smn_read(u16 node, u32 address, u32 *value) +{ + int err = __hygon_smn_rw(node, address, value, false); + if (PCI_POSSIBLE_ERROR(*value)) { + err = -ENODEV; + *value = 0; + } + + return err; +} +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 struct pci_dev *next_northbridge(struct pci_dev *dev, const struct pci_device_id *ids) { -- Gitee From 9bd2ff13bc236e7170f352fa392703a22394e77c Mon Sep 17 00:00:00 2001 From: Wenhui Fan Date: Thu, 14 May 2026 11:31:28 +0800 Subject: [PATCH 03/12] anolis:x86/hygon_nb: Add an interface to convert Hygon PCI devices to node IDs ANBZ: #35240 Add a new interface that determines which node a device belongs to based on its BDF(Bus, device, Function), the interface will be used by other drivers to find correct node ID for SMN access. Hygon-SIG: commit none hygon anolis:x86/hygon_nb: Add an interface to convert Hygon PCI devices to node IDs Signed-off-by: Wenhui Fan --- arch/x86/include/asm/hygon/hygon_nb.h | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/arch/x86/include/asm/hygon/hygon_nb.h b/arch/x86/include/asm/hygon/hygon_nb.h index f5d1e65b8184..5374fc97490a 100644 --- a/arch/x86/include/asm/hygon/hygon_nb.h +++ b/arch/x86/include/asm/hygon/hygon_nb.h @@ -24,6 +24,23 @@ int northbridge_init_hygon(void); u16 hygon_nb_num(void); struct hygon_northbridge *node_to_hygon_nb(int node); +static inline u16 hygon_pci_dev_to_node_id(struct pci_dev *pdev) +{ + struct pci_dev *misc; + int i; + + for (i = 0; i != hygon_nb_num(); i++) { + misc = node_to_hygon_nb(i)->misc; + + if (pci_domain_nr(misc->bus) == pci_domain_nr(pdev->bus) && + PCI_SLOT(misc->devfn) == PCI_SLOT(pdev->devfn)) + return i; + } + + WARN(1, "Unable to find Hygon Northbridge id for %s\n", pci_name(pdev)); + return 0; +} + #else #define northbridge_init_hygon(x) 0 -- Gitee From f14dc1139dee58336f5873a6059d4653616984b1 Mon Sep 17 00:00:00 2001 From: Wenhui Fan Date: Thu, 14 May 2026 11:38:02 +0800 Subject: [PATCH 04/12] anolis:x86/hygon_nb: Add northbridge support for Hygon family 18h model 4h ANBZ: #35240 Add support for Hygon family 18h model 4h processors which have multiple PCI root complexes per data fabric/system management network interface. If there are (N) multiple PCI roots per DF/SMN interface, then the PCI roots are redundant (as far as SMN/DF access goes). x86/hygon_nb: Add northbridge support for Hygon family 18h model 4h Signed-off-by: Wenhui Fan --- arch/x86/include/asm/hygon/hygon_nb.h | 7 + arch/x86/kernel/amd_nb.c | 11 -- arch/x86/kernel/hygon_nb.c | 178 ++++++++++++++++++++++++-- include/linux/pci_ids.h | 1 + 4 files changed, 178 insertions(+), 19 deletions(-) diff --git a/arch/x86/include/asm/hygon/hygon_nb.h b/arch/x86/include/asm/hygon/hygon_nb.h index 5374fc97490a..b795ea5bc9e4 100644 --- a/arch/x86/include/asm/hygon/hygon_nb.h +++ b/arch/x86/include/asm/hygon/hygon_nb.h @@ -23,6 +23,9 @@ int hygon_smn_write(u16 node, u32 address, u32 value); int northbridge_init_hygon(void); u16 hygon_nb_num(void); struct hygon_northbridge *node_to_hygon_nb(int node); +bool hygon_f18h_m4h(void); +int get_df_id(struct pci_dev *misc, u8 *id); +u16 hygon_node_num(void); static inline u16 hygon_pci_dev_to_node_id(struct pci_dev *pdev) { @@ -45,6 +48,10 @@ static inline u16 hygon_pci_dev_to_node_id(struct pci_dev *pdev) #define northbridge_init_hygon(x) 0 #define hygon_nb_num(x) 0 +#define hygon_f18h_m4h false +#define get_df_id(x, y) NULL +#define hygon_node_num(x) + static inline struct hygon_northbridge *node_to_hygon_nb(int node) { return NULL; diff --git a/arch/x86/kernel/amd_nb.c b/arch/x86/kernel/amd_nb.c index 5a0631789742..b0ce5296bf5f 100644 --- a/arch/x86/kernel/amd_nb.c +++ b/arch/x86/kernel/amd_nb.c @@ -62,17 +62,6 @@ struct amd_northbridge *node_to_amd_nb(int node) } EXPORT_SYMBOL_GPL(node_to_amd_nb); -static struct pci_dev *next_northbridge(struct pci_dev *dev, - const struct pci_device_id *ids) -{ - do { - dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev); - if (!dev) - break; - } while (!pci_match_id(ids, dev)); - return dev; -} - static int amd_cache_northbridges(void) { struct amd_northbridge *nb; diff --git a/arch/x86/kernel/hygon_nb.c b/arch/x86/kernel/hygon_nb.c index 69ab322cf8d5..078c1eb02185 100644 --- a/arch/x86/kernel/hygon_nb.c +++ b/arch/x86/kernel/hygon_nb.c @@ -16,9 +16,13 @@ #include #define PCI_DEVICE_ID_HYGON_18H_ROOT 0x1450 +#define PCI_DEVICE_ID_HYGON_18H_M04H_ROOT 0x1480 +#define PCI_DEVICE_ID_HYGON_18H_M04H_DF_F1 0x1491 #define PCI_DEVICE_ID_HYGON_18H_DF_F4 0x1464 +#define PCI_DEVICE_ID_HYGON_18H_M04H_DF_F4 0x1494 +static u16 node_num; static struct pci_dev **hygon_roots; static struct hygon_northbridge_info hygon_northbridges; @@ -27,16 +31,19 @@ static DEFINE_MUTEX(smn_mutex); static const struct pci_device_id hygon_root_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_HYGON, PCI_DEVICE_ID_HYGON_18H_ROOT) }, + { PCI_DEVICE(PCI_VENDOR_ID_HYGON, PCI_DEVICE_ID_HYGON_18H_M04H_ROOT) }, {} }; 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) }, {} }; 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) }, {} }; @@ -105,6 +112,12 @@ static struct pci_dev *next_northbridge(struct pci_dev *dev, return dev; } +u16 hygon_node_num(void) +{ + return node_num; +} +EXPORT_SYMBOL_GPL(hygon_node_num); + u16 hygon_nb_num(void) { return hygon_northbridges.num; @@ -117,16 +130,96 @@ struct hygon_northbridge *node_to_hygon_nb(int node) } EXPORT_SYMBOL_GPL(node_to_hygon_nb); +static int get_df_register(struct pci_dev *misc, u8 func, int offset, u32 *value) +{ + struct pci_dev *df_func= NULL; + u32 device; + int err; + + if (func == 1) { + switch (boot_cpu_data.x86_model) { + case 0x4: + device = PCI_DEVICE_ID_HYGON_18H_M04H_DF_F1; + break; + default: + return -ENODEV; + } + } else { + return -ENODEV; + } + + while ((df_func= pci_get_device(misc->vendor, device, df_func))) + if (pci_domain_nr(df_func->bus) == pci_domain_nr(misc->bus) && + df_func->bus->number == misc->bus->number && + PCI_SLOT(df_func->devfn) == PCI_SLOT(misc->devfn)) + break; + + if (!df_func) { + pr_warn("Error getting DF func device.\n"); + return -ENODEV; + } + + err = pci_read_config_dword(df_func, offset, value); + if (err) + pr_warn("Error reading DF func register.\n"); + + return err; +} + +bool hygon_f18h_m4h(void) +{ + if (boot_cpu_data.x86_vendor != X86_VENDOR_HYGON) + return false; + + if (boot_cpu_data.x86 == 0x18 && + boot_cpu_data.x86_model >= 0x4 && + boot_cpu_data.x86_model <= 0xf) + return true; + + return false; +} +EXPORT_SYMBOL_GPL(hygon_f18h_m4h); + +int get_df_id(struct pci_dev *misc, u8 *id) +{ + u32 value; + int ret; + + /* F1x200[23:20]: DF ID */ + ret = get_df_register(misc, 1, 0x200, &value); + *id = (value >> 20) & 0xf; + + return ret; +} +EXPORT_SYMBOL_GPL(get_df_id); + +static u8 get_socket_num(struct pci_dev *misc) +{ + u32 value; + int ret; + + /* F1x200[7:0]: Which socket is present. */ + ret = get_df_register(misc, 1, 0x200, &value); + + return ret ? 0 : hweight8(value & 0xff); +} + int northbridge_init_hygon(void) { const struct pci_device_id *misc_ids = hygon_nb_misc_ids; const struct pci_device_id *link_ids = hygon_nb_link_ids; const struct pci_device_id *root_ids = hygon_root_ids; struct pci_dev *root, *misc, *link; + struct pci_dev *root_first = NULL; struct hygon_northbridge *nb; + u16 roots_per_socket = 0; + u16 miscs_per_socket = 0; + u16 socket_num = 0; + u16 root_count = 0; u16 misc_count = 0; int err = -ENODEV; - u16 i = 0; + u8 i, j, m, n; + u8 id; if (hygon_northbridges.num) return 0; @@ -135,11 +228,43 @@ int northbridge_init_hygon(void) while ((misc = next_northbridge(misc, misc_ids))) misc_count++; - if (!misc_count) { + root = NULL; + while ((root = next_northbridge(root, root_ids)) != NULL) + root_count++; + + if (!root_count || !misc_count) { err = -ENODEV; goto out; } + if (hygon_f18h_m4h()) { + misc = NULL; + misc = next_northbridge(NULL, misc_ids); + if (misc != NULL) { + socket_num = get_socket_num(misc); + pr_info("Socket number: %d\n", socket_num); + if (!socket_num) { + err = -ENODEV; + goto out; + } + } else { + err = -ENODEV; + goto out; + } + + /* + * There should be _exactly_ N roots for each DF/SMN + * interface, and M DF/SMN interfaces in one socket. + */ + roots_per_socket = root_count / socket_num; + miscs_per_socket = misc_count / socket_num; + + if (!roots_per_socket || !miscs_per_socket) { + err = -ENODEV; + goto out; + } + } + hygon_roots = kcalloc(misc_count, sizeof(*hygon_roots), GFP_KERNEL); if (!hygon_roots) { err = -ENOMEM; @@ -156,20 +281,57 @@ int northbridge_init_hygon(void) hygon_northbridges.num = misc_count; link = misc = root = NULL; + j = m = n = 0; for (i = 0; i < hygon_northbridges.num; i++) { - hygon_roots[i] = root = - next_northbridge(root, root_ids); - node_to_hygon_nb(i)->misc = misc = - next_northbridge(misc, misc_ids); - node_to_hygon_nb(i)->link = link = - next_northbridge(link, link_ids); + misc = next_northbridge(misc, misc_ids); + link = next_northbridge(link, link_ids); + if (hygon_f18h_m4h()) { + /* Only save the first PCI root device for each socket. */ + if (!(i % miscs_per_socket)) { + root = root_first = next_northbridge(root, root_ids); + j = 1; + } + + if (get_df_id(misc, &id)) { + err = -ENODEV; + goto err_free_nb; + } + pr_info("DF ID: %d\n", id); + + if (id < 4) { + /* Add the devices with id<4 from the tail. */ + node_to_hygon_nb(misc_count - m - 1)->misc = misc; + node_to_hygon_nb(misc_count - m - 1)->link = link; + hygon_roots[misc_count - m - 1] = root_first; + m++; + } else { + node_to_hygon_nb(n)->misc = misc; + node_to_hygon_nb(n)->link = link; + hygon_roots[n] = root_first; + n++; + } + + /* Skip the redundant PCI root devices per socket. */ + while (j < roots_per_socket) { + root = next_northbridge(root, root_ids); + j++; + } + } else { + hygon_roots[i] = root = next_northbridge(root, root_ids); + node_to_hygon_nb(i)->misc = misc; + node_to_hygon_nb(i)->link = link; + n++; + } } + node_num = n; pr_info("Hygon Fam%xh Model%xh NB driver init success.\n", boot_cpu_data.x86, boot_cpu_data.x86_model); return 0; +err_free_nb: + kfree(nb); err_free_roots: kfree(hygon_roots); diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index ed7c9f85427c..397ae63d5f8f 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2605,6 +2605,7 @@ #define PCI_DEVICE_ID_HYGON_18H_M10H_HDA 0x14c9 #define PCI_DEVICE_ID_HYGON_18H_M18H_HDA 0x2007 #define PCI_DEVICE_ID_HYGON_18H_DF_F3 0x1463 +#define PCI_DEVICE_ID_HYGON_18H_M04H_DF_F3 0x1493 #define PCI_DEVICE_ID_HYGON_18H_M05H_DF_F3 0x14b3 #define PCI_DEVICE_ID_HYGON_18H_M10H_DF_F3 0x14d3 #define PCI_DEVICE_ID_HYGON_18H_M18H_DF_F3 0x2013 -- Gitee From c080f92d16ef69ab4e91ce7314d8a6e23a32dfbb Mon Sep 17 00:00:00 2001 From: Wenhui Fan Date: Thu, 14 May 2026 11:43:21 +0800 Subject: [PATCH 05/12] anolis:x86/hygon_nb:Add support for Hygon family 18h model 5h ANBZ: #35240 Add root and DF F1/F3/F4 device IDs for Hygon family 18h model 5h processors. But some model 5h processors have the legacy(M04H) DF devices, so add a if conditional to read the df1 register. Hygon-SIG: commit none hygon anolis:x86/hygon_nb:Add support for Hygon family 18h model 5h Signed-off-by: Wenhui Fan --- arch/x86/kernel/hygon_nb.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/arch/x86/kernel/hygon_nb.c b/arch/x86/kernel/hygon_nb.c index 078c1eb02185..ee6ece6862e3 100644 --- a/arch/x86/kernel/hygon_nb.c +++ b/arch/x86/kernel/hygon_nb.c @@ -16,11 +16,14 @@ #include #define PCI_DEVICE_ID_HYGON_18H_ROOT 0x1450 +#define PCI_DEVICE_ID_HYGON_18H_M05H_ROOT 0x14a0 #define PCI_DEVICE_ID_HYGON_18H_M04H_ROOT 0x1480 #define PCI_DEVICE_ID_HYGON_18H_M04H_DF_F1 0x1491 +#define PCI_DEVICE_ID_HYGON_18H_M05H_DF_F1 0x14b1 #define PCI_DEVICE_ID_HYGON_18H_DF_F4 0x1464 #define PCI_DEVICE_ID_HYGON_18H_M04H_DF_F4 0x1494 +#define PCI_DEVICE_ID_HYGON_18H_M05H_DF_F4 0x14b4 static u16 node_num; static struct pci_dev **hygon_roots; @@ -32,18 +35,21 @@ static DEFINE_MUTEX(smn_mutex); static const struct pci_device_id hygon_root_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_HYGON, PCI_DEVICE_ID_HYGON_18H_ROOT) }, { PCI_DEVICE(PCI_VENDOR_ID_HYGON, PCI_DEVICE_ID_HYGON_18H_M04H_ROOT) }, + { PCI_DEVICE(PCI_VENDOR_ID_HYGON, PCI_DEVICE_ID_HYGON_18H_M05H_ROOT) }, {} }; 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) }, {} }; 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) }, {} }; @@ -141,6 +147,12 @@ static int get_df_register(struct pci_dev *misc, u8 func, int offset, u32 *valu case 0x4: device = PCI_DEVICE_ID_HYGON_18H_M04H_DF_F1; break; + case 0x5: + if (misc->device == PCI_DEVICE_ID_HYGON_18H_M05H_DF_F3) + device = PCI_DEVICE_ID_HYGON_18H_M05H_DF_F1; + else + device = PCI_DEVICE_ID_HYGON_18H_M04H_DF_F1; + break; default: return -ENODEV; } -- Gitee From 53c3918b828775fc19da0489cad1bfad110bb23d Mon Sep 17 00:00:00 2001 From: Wenhui Fan Date: Thu, 14 May 2026 11:45:19 +0800 Subject: [PATCH 06/12] anolis:x86/hygon_nb: Add support for Hygon family 18h model 6h~8h ANBZ: #35240 Hygon family 18h model 6h~8h processor has the same DF F1 device ID as M05H_DF_F1, but should get DF ID from DF F5 device. Hygon-SIG: commit none hygon anolis:x86/hygon_nb: Add support for Hygon family 18h model 6h~8h Signed-off-by: Wenhui Fan --- arch/x86/kernel/hygon_nb.c | 25 ++++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/arch/x86/kernel/hygon_nb.c b/arch/x86/kernel/hygon_nb.c index ee6ece6862e3..5937cf30399e 100644 --- a/arch/x86/kernel/hygon_nb.c +++ b/arch/x86/kernel/hygon_nb.c @@ -24,6 +24,7 @@ #define PCI_DEVICE_ID_HYGON_18H_DF_F4 0x1464 #define PCI_DEVICE_ID_HYGON_18H_M04H_DF_F4 0x1494 #define PCI_DEVICE_ID_HYGON_18H_M05H_DF_F4 0x14b4 +#define PCI_DEVICE_ID_HYGON_18H_M06H_DF_F5 0x14b5 static u16 node_num; static struct pci_dev **hygon_roots; @@ -153,6 +154,17 @@ static int get_df_register(struct pci_dev *misc, u8 func, int offset, u32 *valu else device = PCI_DEVICE_ID_HYGON_18H_M04H_DF_F1; break; + case 0x6 ... 0x8: + device = PCI_DEVICE_ID_HYGON_18H_M05H_DF_F1; + break; + default: + return -ENODEV; + } + } else if (func == 5) { + switch (boot_cpu_data.x86_model) { + case 0x6 ... 0x8: + device = PCI_DEVICE_ID_HYGON_18H_M06H_DF_F5; + break; default: return -ENODEV; } @@ -197,9 +209,16 @@ int get_df_id(struct pci_dev *misc, u8 *id) u32 value; int ret; - /* F1x200[23:20]: DF ID */ - ret = get_df_register(misc, 1, 0x200, &value); - *id = (value >> 20) & 0xf; + if (boot_cpu_data.x86_model >= 0x6 && + boot_cpu_data.x86_model <= 0xf) { + /* F5x180[19:16]: DF ID */ + ret = get_df_register(misc, 5, 0x180, &value); + *id = (value >> 16) & 0xf; + } else { + /* F1x200[23:20]: DF ID */ + ret = get_df_register(misc, 1, 0x200, &value); + *id = (value >> 20) & 0xf; + } return ret; } -- Gitee From d3ee1a173b3b8c9f01cad9dd90bedb6b66292b08 Mon Sep 17 00:00:00 2001 From: Wenhui Fan Date: Fri, 15 May 2026 11:42:08 +0800 Subject: [PATCH 07/12] anolis:x86/hygon_nb: Add support for Hygon family 18h model 10h ANBZ: #35240 Add hygon_f18h_m10h() to identify Hygon family 18h model 10h processors. Hygon-SIG: commit none hygon anolis:x86/hygon_nb: Add support for Hygon family 18h model 10h Signed-off-by: Wenhui Fan --- arch/x86/include/asm/hygon/hygon_nb.h | 2 ++ arch/x86/kernel/hygon_nb.c | 14 ++++++++++++++ 2 files changed, 16 insertions(+) diff --git a/arch/x86/include/asm/hygon/hygon_nb.h b/arch/x86/include/asm/hygon/hygon_nb.h index b795ea5bc9e4..7eeaf77dc98a 100644 --- a/arch/x86/include/asm/hygon/hygon_nb.h +++ b/arch/x86/include/asm/hygon/hygon_nb.h @@ -24,6 +24,7 @@ int northbridge_init_hygon(void); u16 hygon_nb_num(void); struct hygon_northbridge *node_to_hygon_nb(int node); bool hygon_f18h_m4h(void); +bool hygon_f18h_m10h(void); int get_df_id(struct pci_dev *misc, u8 *id); u16 hygon_node_num(void); @@ -49,6 +50,7 @@ static inline u16 hygon_pci_dev_to_node_id(struct pci_dev *pdev) #define northbridge_init_hygon(x) 0 #define hygon_nb_num(x) 0 #define hygon_f18h_m4h false +#define hygon_f18h_m10h false #define get_df_id(x, y) NULL #define hygon_node_num(x) diff --git a/arch/x86/kernel/hygon_nb.c b/arch/x86/kernel/hygon_nb.c index 5937cf30399e..9acc4c37298a 100644 --- a/arch/x86/kernel/hygon_nb.c +++ b/arch/x86/kernel/hygon_nb.c @@ -204,6 +204,20 @@ bool hygon_f18h_m4h(void) } EXPORT_SYMBOL_GPL(hygon_f18h_m4h); +bool hygon_f18h_m10h(void) +{ + if (boot_cpu_data.x86_vendor != X86_VENDOR_HYGON) + return false; + + if (boot_cpu_data.x86 == 0x18 && + boot_cpu_data.x86_model >= 0x10 && + boot_cpu_data.x86_model <= 0x1f) + return true; + + return false; +} +EXPORT_SYMBOL_GPL(hygon_f18h_m10h); + int get_df_id(struct pci_dev *misc, u8 *id) { u32 value; -- Gitee From ccca24d33112871dfa7656ee9c1e94195e741ddc Mon Sep 17 00:00:00 2001 From: Wenhui Fan Date: Sat, 16 May 2026 17:39:42 +0800 Subject: [PATCH 08/12] anolis:hwmon/k10temp: Update the SMN interface used by Hygon processors in k10temp ANBZ: #35240 The Hygon NB driver has been separated form AMD NB, and the SMN interface is now implemented separately. Therefore, the SMN interface used by Hygon must be updated accordingly. Hygon-SIG: commit none hygon anolis:hwmon/k10temp: Update the SMN interface used by Hygon processors in the k10temp Signed-off-by: Wenhui Fan --- drivers/hwmon/k10temp.c | 49 ++++++++++++++++++++++++++++------------- 1 file changed, 34 insertions(+), 15 deletions(-) diff --git a/drivers/hwmon/k10temp.c b/drivers/hwmon/k10temp.c index f7fe33110274..e5ae2c2e8e25 100644 --- a/drivers/hwmon/k10temp.c +++ b/drivers/hwmon/k10temp.c @@ -23,6 +23,7 @@ #include #include #include +#include MODULE_DESCRIPTION("AMD Family 10h+ CPU core temperature monitor"); MODULE_AUTHOR("Clemens Ladisch "); @@ -177,6 +178,20 @@ static void read_tempreg_nb_zen(struct pci_dev *pdev, u32 *regval) *regval = 0; } +static void read_tempreg_nb_hygon(struct pci_dev *pdev, u32 *regval) +{ + if (hygon_smn_read(hygon_pci_dev_to_node_id(pdev), + ZEN_REPORTED_TEMP_CTRL_BASE, regval)) + *regval = 0; +} + +static int read_hygon_ccd_temp_reg(struct k10temp_data *data, int ccd, u32 *regval) +{ + u16 node_id = hygon_pci_dev_to_node_id(data->pdev); + + return hygon_smn_read(node_id, ZEN_CCD_TEMP(data->ccd_offset, ccd), regval); +} + static long get_raw_temp(struct k10temp_data *data) { u32 regval; @@ -228,16 +243,14 @@ static void hygon_read_temp(struct k10temp_data *data, int channel, h_priv = (struct hygon_private *)data->priv; if ((channel - 2) < h_priv->index_2nd) { - if (amd_smn_read(amd_pci_dev_to_node_id(data->pdev), + hygon_smn_read(hygon_pci_dev_to_node_id(data->pdev), ZEN_CCD_TEMP(data->ccd_offset, channel - 2), - regval)) - *regval = 0; + regval); } else { - if (amd_smn_read(amd_pci_dev_to_node_id(data->pdev), + hygon_smn_read(hygon_pci_dev_to_node_id(data->pdev), ZEN_CCD_TEMP(h_priv->offset_2nd, channel - 2 - h_priv->index_2nd), - regval)) - *regval = 0; + regval); } } @@ -438,9 +451,15 @@ static void k10temp_get_ccd_support(struct pci_dev *pdev, * the register value. And this will incorrectly pass the TEMP_VALID * bit check. */ - if (amd_smn_read(amd_pci_dev_to_node_id(pdev), - ZEN_CCD_TEMP(data->ccd_offset, i), ®val)) - continue; + if (boot_cpu_data.x86_vendor == X86_VENDOR_HYGON && + boot_cpu_data.x86 == 0x18) { + if (read_hygon_ccd_temp_reg(data, i, ®val)) + continue; + } else { + if (amd_smn_read(amd_pci_dev_to_node_id(pdev), + ZEN_CCD_TEMP(data->ccd_offset, i), ®val)) + continue; + } if (regval & ZEN_CCD_TEMP_VALID) data->show_temp |= BIT(TCCD_BIT(i)); @@ -456,10 +475,10 @@ static void k10temp_get_ccd_support_2nd(struct pci_dev *pdev, h_priv = (struct hygon_private *)data->priv; for (i = h_priv->index_2nd; i < limit; i++) { - if (amd_smn_read(amd_pci_dev_to_node_id(pdev), - ZEN_CCD_TEMP(h_priv->offset_2nd, - i - h_priv->index_2nd), - ®val)) + if (hygon_smn_read(hygon_pci_dev_to_node_id(pdev), + ZEN_CCD_TEMP(h_priv->offset_2nd, + i - h_priv->index_2nd), + ®val)) continue; if (regval & ZEN_CCD_TEMP_VALID) data->show_temp |= BIT(TCCD_BIT(i)); @@ -545,7 +564,7 @@ static int k10temp_probe(struct pci_dev *pdev, const struct pci_device_id *id) } else if (boot_cpu_data.x86_vendor == X86_VENDOR_HYGON && boot_cpu_data.x86 == 0x18) { data->temp_adjust_mask = ZEN_CUR_TEMP_RANGE_SEL_MASK; - data->read_tempreg = read_tempreg_nb_zen; + data->read_tempreg = read_tempreg_nb_hygon; data->is_zen = true; if (boot_cpu_data.x86_model >= 0x4 && @@ -645,7 +664,7 @@ static const struct pci_device_id k10temp_id_table[] = { { 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) }, - { PCI_VDEVICE(HYGON, PCI_DEVICE_ID_AMD_17H_M30H_DF_F3) }, + { PCI_VDEVICE(HYGON, PCI_DEVICE_ID_HYGON_18H_M04H_DF_F3) }, { PCI_VDEVICE(HYGON, PCI_DEVICE_ID_HYGON_18H_M05H_DF_F3) }, { PCI_VDEVICE(HYGON, PCI_DEVICE_ID_HYGON_18H_M10H_DF_F3) }, { PCI_VDEVICE(HYGON, PCI_DEVICE_ID_HYGON_18H_M18H_DF_F3) }, -- Gitee From 3ce672af9d1a86a7bc69d150acaccf4cc88f96cb Mon Sep 17 00:00:00 2001 From: Wenhui Fan Date: Tue, 19 May 2026 15:14:27 +0800 Subject: [PATCH 09/12] anolis:EDAC/amd64:Update the SMN and nb interface used by Hygon processors in EDAC ANBZ: #35240 Separate some Hygon EDAC function from AMD EDAC function because there are many interface changed. Hygon-SIG: commit none hygon anolis:EDAC/amd64:Update the SMN and nb interface used by Hygon processors in EDAC Signed-off-by: Wenhui Fan --- drivers/edac/amd64_edac.c | 146 ++++++++++++++++++++++++++++++++------ 1 file changed, 124 insertions(+), 22 deletions(-) diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index d4d2d08d14f1..3b49932d0165 100644 --- a/drivers/edac/amd64_edac.c +++ b/drivers/edac/amd64_edac.c @@ -3,6 +3,7 @@ #include "amd64_edac.h" #include #include +#include static struct edac_pci_ctl_info *pci_ctl; @@ -99,7 +100,7 @@ int __amd64_write_pci_cfg_dword(struct pci_dev *pdev, int offset, static u32 get_umc_base_f18h_m4h(u16 node, u8 channel) { - struct pci_dev *f3 = node_to_amd_nb(node)->misc; + struct pci_dev *f3 = node_to_hygon_nb(node)->misc; u8 df_id; get_df_id(f3, &df_id); @@ -1102,10 +1103,10 @@ static int __df_indirect_read(u16 node, u8 func, u16 reg, u8 instance_id, u32 *l u32 ficaa; int err = -ENODEV; - if (node >= amd_nb_num()) + if (node >= hygon_nb_num()) goto out; - F4 = node_to_amd_nb(node)->link; + F4 = node_to_hygon_nb(node)->link; if (!F4) goto out; @@ -1803,7 +1804,7 @@ static void umc_prep_chip_selects(struct amd64_pvt *pvt) } } -static void umc_read_base_mask(struct amd64_pvt *pvt) +static void hygon_umc_read_base_mask(struct amd64_pvt *pvt) { u32 umc_base_reg, umc_base_reg_sec; u32 umc_mask_reg, umc_mask_reg_sec; @@ -1819,10 +1820,70 @@ static void umc_read_base_mask(struct amd64_pvt *pvt) if (!hygon_umc_channel_enabled(pvt, umc)) continue; - if (boot_cpu_data.x86_vendor == X86_VENDOR_HYGON) - umc_base = hygon_get_umc_base(pvt, umc); - else - umc_base = get_umc_base(umc); + umc_base = hygon_get_umc_base(pvt, umc); + + umc_base_reg = umc_base + UMCCH_BASE_ADDR; + umc_base_reg_sec = umc_base + UMCCH_BASE_ADDR_SEC; + + for_each_chip_select(cs, umc, pvt) { + base = &pvt->csels[umc].csbases[cs]; + base_sec = &pvt->csels[umc].csbases_sec[cs]; + + base_reg = umc_base_reg + (cs * 4); + base_reg_sec = umc_base_reg_sec + (cs * 4); + + if (!hygon_smn_read(pvt->mc_node_id, base_reg, &tmp)) { + *base = tmp; + edac_dbg(0, " DCSB%d[%d]=0x%08x reg: 0x%x\n", + umc, cs, *base, base_reg); + } + + if (!hygon_smn_read(pvt->mc_node_id, base_reg_sec, &tmp)) { + *base_sec = tmp; + edac_dbg(0, " DCSB_SEC%d[%d]=0x%08x reg: 0x%x\n", + umc, cs, *base_sec, base_reg_sec); + } + } + + umc_mask_reg = umc_base + UMCCH_ADDR_MASK; + umc_mask_reg_sec = umc_base + UMCCH_ADDR_MASK_SEC; + + for_each_chip_select_mask(cs, umc, pvt) { + mask = &pvt->csels[umc].csmasks[cs]; + mask_sec = &pvt->csels[umc].csmasks_sec[cs]; + + mask_reg = umc_mask_reg + (cs * 4); + mask_reg_sec = umc_mask_reg_sec + (cs * 4); + + if (!hygon_smn_read(pvt->mc_node_id, mask_reg, &tmp)) { + *mask = tmp; + edac_dbg(0, " DCSM%d[%d]=0x%08x reg: 0x%x\n", + umc, cs, *mask, mask_reg); + } + + if (!hygon_smn_read(pvt->mc_node_id, mask_reg_sec, &tmp)) { + *mask_sec = tmp; + edac_dbg(0, " DCSM_SEC%d[%d]=0x%08x reg: 0x%x\n", + umc, cs, *mask_sec, mask_reg_sec); + } + } + } +} + +static void umc_read_base_mask(struct amd64_pvt *pvt) +{ + u32 umc_base_reg, umc_base_reg_sec; + u32 umc_mask_reg, umc_mask_reg_sec; + u32 base_reg, base_reg_sec; + u32 mask_reg, mask_reg_sec; + u32 *base, *base_sec; + u32 *mask, *mask_sec; + u32 umc_base; + int cs, umc; + u32 tmp; + + for_each_umc(umc) { + umc_base = get_umc_base(umc); umc_base_reg = umc_base + UMCCH_BASE_ADDR; umc_base_reg_sec = umc_base + UMCCH_BASE_ADDR_SEC; @@ -3299,6 +3360,38 @@ static void determine_ecc_sym_sz(struct amd64_pvt *pvt) } } +static void hygon_umc_read_mc_regs(struct amd64_pvt *pvt) +{ + u8 nid = pvt->mc_node_id; + struct amd64_umc *umc; + u32 i, tmp, umc_base; + + /* Read registers from each UMC */ + for_each_umc(i) { + if (!hygon_umc_channel_enabled(pvt, i)) + continue; + + umc_base = hygon_get_umc_base(pvt, i); + + umc = &pvt->umc[i]; + + if (!hygon_smn_read(nid, umc_base + UMCCH_DIMM_CFG, &tmp)) + umc->dimm_cfg = tmp; + + if (!hygon_smn_read(nid, umc_base + UMCCH_UMC_CFG, &tmp)) + umc->umc_cfg = tmp; + + if (!hygon_smn_read(nid, umc_base + UMCCH_SDP_CTRL, &tmp)) + umc->sdp_ctrl = tmp; + + if (!hygon_smn_read(nid, umc_base + UMCCH_ECC_CTRL, &tmp)) + umc->ecc_ctrl = tmp; + + if (!hygon_smn_read(nid, umc_base + UMCCH_UMC_CAP_HI, &tmp)) + umc->umc_cap_hi = tmp; + } +} + /* * Retrieve the hardware registers of the memory controller. */ @@ -3310,13 +3403,7 @@ static void umc_read_mc_regs(struct amd64_pvt *pvt) /* Read registers from each UMC */ for_each_umc(i) { - if (!hygon_umc_channel_enabled(pvt, i)) - continue; - - if (boot_cpu_data.x86_vendor == X86_VENDOR_HYGON) - umc_base = hygon_get_umc_base(pvt, i); - else - umc_base = get_umc_base(i); + umc_base = get_umc_base(i); umc = &pvt->umc[i]; @@ -3888,8 +3975,14 @@ static int umc_hw_info_get(struct amd64_pvt *pvt) return -ENOMEM; umc_prep_chip_selects(pvt); - umc_read_base_mask(pvt); - umc_read_mc_regs(pvt); + + if (boot_cpu_data.x86_vendor == X86_VENDOR_HYGON) { + hygon_umc_read_base_mask(pvt); + hygon_umc_read_mc_regs(pvt); + } else { + umc_read_base_mask(pvt); + umc_read_mc_regs(pvt); + } umc_determine_memory_type(pvt); return 0; @@ -4429,7 +4522,7 @@ static bool instance_has_memory(struct amd64_pvt *pvt) static int probe_one_instance(unsigned int nid) { - struct pci_dev *F3 = node_to_amd_nb(nid)->misc; + struct pci_dev *F3; struct amd64_pvt *pvt = NULL; struct ecc_settings *s; int ret; @@ -4446,6 +4539,10 @@ static int probe_one_instance(unsigned int nid) goto err_settings; pvt->mc_node_id = nid; + if (boot_cpu_data.x86_vendor == X86_VENDOR_HYGON) + F3 = node_to_hygon_nb(nid)->misc; + else + F3 = node_to_amd_nb(nid)->misc; pvt->F3 = F3; ret = per_family_init(pvt); @@ -4509,11 +4606,16 @@ static int probe_one_instance(unsigned int nid) static void remove_one_instance(unsigned int nid) { - struct pci_dev *F3 = node_to_amd_nb(nid)->misc; + struct pci_dev *F3; struct ecc_settings *s = ecc_stngs[nid]; struct mem_ctl_info *mci; struct amd64_pvt *pvt; + if (boot_cpu_data.x86_vendor == X86_VENDOR_HYGON) + F3 = node_to_hygon_nb(nid)->misc; + else + F3 = node_to_amd_nb(nid)->misc; + /* Remove from EDAC CORE tracking list */ mci = edac_mc_del_mc(&F3->dev); if (!mci) @@ -4576,13 +4678,13 @@ static int __init amd64_edac_init(void) if (!x86_match_cpu(amd64_cpuids)) return -ENODEV; - if (!amd_nb_num()) + if (!amd_nb_num() && !hygon_node_num()) return -ENODEV; opstate_init(); if (hygon_f18h_m4h()) - instance_num = hygon_nb_num(); + instance_num = hygon_node_num(); else instance_num = amd_nb_num(); @@ -4653,7 +4755,7 @@ static void __exit amd64_edac_exit(void) amd_unregister_ecc_decoder(decode_bus_error); if (hygon_f18h_m4h()) - instance_num = hygon_nb_num(); + instance_num = hygon_node_num(); else instance_num = amd_nb_num(); -- Gitee From 34195b6440063f87351bed90cd235d7b9dc359f7 Mon Sep 17 00:00:00 2001 From: Wenhui Fan Date: Tue, 19 May 2026 20:04:08 +0800 Subject: [PATCH 10/12] anolis: EDAC/amd64: Rename the address translation function for hygon family 18h model 4h ANBZ: #35240 The funciton umc_normaddr_to_sysaddr() is only used by hygon family 18h model 4h in current devel-5.10 branch, so rename it with 'hygon_' prefix to avoid misunderstanding and remove the undeeded if branch with hygon_f18h_m4h() in the function. Hygon-SIG: commit none hygon anolis: EDAC/amd64: Rename the address translation function for hygon family 18h model 4h Signed-off-by: Wenhui Fan --- drivers/edac/amd64_edac.c | 60 ++++++++++----------------------------- 1 file changed, 15 insertions(+), 45 deletions(-) diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index 3b49932d0165..5996df6ccf28 100644 --- a/drivers/edac/amd64_edac.c +++ b/drivers/edac/amd64_edac.c @@ -1151,21 +1151,17 @@ struct addr_ctx { u8 inst_id; }; -static int umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, u64 *sys_addr) +static int hygon_umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, u64 *sys_addr) { u64 dram_base_addr, dram_limit_addr, dram_hole_base; + u16 die_id_mask, socket_id_mask, cs_id = 0; u8 die_id_shift, socket_id_shift; -#ifdef CONFIG_CPU_SUP_HYGON - u16 die_id_mask, socket_id_mask; -#else - u8 die_id_mask, socket_id_mask; -#endif u8 intlv_num_dies, intlv_num_chan, intlv_num_sockets; u8 intlv_addr_sel, intlv_addr_bit; u8 num_intlv_bits, hashed_bit; u8 lgcy_mmio_hole_en, base = 0; - u8 cs_mask, cs_id = 0; + u8 cs_mask; bool hash_enabled = false; struct addr_ctx ctx; @@ -1179,10 +1175,7 @@ static int umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, u64 *sys_addr ctx.inst_id = umc; /* Read DramOffset, check if base 1 is used. */ - if ((hygon_f18h_m4h() || hygon_f18h_m10h()) && - df_indirect_read_instance(nid, 0, 0x214, umc, &ctx.tmp)) - goto out_err; - else if (df_indirect_read_instance(nid, 0, 0x1B4, umc, &ctx.tmp)) + if (df_indirect_read_instance(nid, 0, 0x214, umc, &ctx.tmp)) goto out_err; /* Remove HiAddrOffset from normalized address, if enabled: */ @@ -1206,9 +1199,7 @@ static int umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, u64 *sys_addr goto out_err; } - intlv_num_sockets = 0; - if (hygon_f18h_m4h() || hygon_f18h_m10h()) - intlv_num_sockets = (ctx.tmp >> 2) & 0x3; + intlv_num_sockets = (ctx.tmp >> 2) & 0x3; lgcy_mmio_hole_en = ctx.tmp & BIT(1); intlv_num_chan = (ctx.tmp >> 4) & 0xF; intlv_addr_sel = (ctx.tmp >> 8) & 0x7; @@ -1225,15 +1216,12 @@ static int umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, u64 *sys_addr if (df_indirect_read_instance(nid, 0, 0x114 + (8 * base), umc, &ctx.tmp)) goto out_err; - if (!hygon_f18h_m4h() && !hygon_f18h_m10h()) - intlv_num_sockets = (ctx.tmp >> 8) & 0x1; intlv_num_dies = (ctx.tmp >> 10) & 0x3; dram_limit_addr = ((ctx.tmp & GENMASK_ULL(31, 12)) << 16) | GENMASK_ULL(27, 0); intlv_addr_bit = intlv_addr_sel + 8; - if ((hygon_f18h_m4h() && boot_cpu_data.x86_model >= 0x6) || - hygon_f18h_m10h()) { + if (boot_cpu_data.x86_model >= 0x6) { if (df_indirect_read_instance(nid, 0, 0x60, umc, &ctx.tmp)) goto out_err; intlv_num_dies = ctx.tmp & 0x3; @@ -1251,7 +1239,7 @@ static int umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, u64 *sys_addr hash_enabled = true; break; default: - if (hygon_f18h_m4h() && boot_cpu_data.x86_model == 0x4 && + if (boot_cpu_data.x86_model == 0x4 && intlv_num_chan == 2) break; pr_err("%s: Invalid number of interleaved channels %d.\n", @@ -1273,8 +1261,7 @@ static int umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, u64 *sys_addr num_intlv_bits += intlv_num_sockets; /* Assert num_intlv_bits in the correct range. */ - if ((hygon_f18h_m4h() && num_intlv_bits > 7) || - (!hygon_f18h_m4h() && num_intlv_bits > 4)) { + if (num_intlv_bits > 7) { pr_err("%s: Invalid interleave bits %d.\n", __func__, num_intlv_bits); goto out_err; @@ -1283,11 +1270,7 @@ static int umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, u64 *sys_addr if (num_intlv_bits > 0) { u64 temp_addr_x, temp_addr_i, temp_addr_y; u8 die_id_bit, sock_id_bit; -#ifdef CONFIG_CPU_SUP_HYGON u16 cs_fabric_id; -#else - u8 cs_fabric_id; -#endif /* * Read FabricBlockInstanceInformation3_CS[BlockFabricID]. @@ -1298,10 +1281,7 @@ static int umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, u64 *sys_addr if (df_indirect_read_instance(nid, 0, 0x50, umc, &ctx.tmp)) goto out_err; - if (hygon_f18h_m4h() || hygon_f18h_m10h()) - cs_fabric_id = (ctx.tmp >> 8) & 0x7FF; - else - cs_fabric_id = (ctx.tmp >> 8) & 0xFF; + cs_fabric_id = (ctx.tmp >> 8) & 0x7FF; die_id_bit = 0; /* If interleaved over more than 1 channel: */ @@ -1321,26 +1301,16 @@ static int umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, u64 *sys_addr /* If interleaved over more than 1 die. */ if (intlv_num_dies) { sock_id_bit = die_id_bit + intlv_num_dies; - if (hygon_f18h_m4h()) { - die_id_shift = (ctx.tmp >> 12) & 0xF; - die_id_mask = ctx.tmp & 0x7FF; - cs_id |= (((cs_fabric_id & die_id_mask) >> die_id_shift) - 4) << - die_id_bit; - } else { - die_id_shift = (ctx.tmp >> 24) & 0xF; - die_id_mask = (ctx.tmp >> 8) & 0xFF; - cs_id |= ((cs_fabric_id & die_id_mask) >> die_id_shift) << - die_id_bit; - } + die_id_shift = (ctx.tmp >> 12) & 0xF; + die_id_mask = ctx.tmp & 0x7FF; + cs_id |= (((cs_fabric_id & die_id_mask) >> die_id_shift) - 4) << + die_id_bit; } /* If interleaved over more than 1 socket. */ if (intlv_num_sockets) { socket_id_shift = (ctx.tmp >> 28) & 0xF; - if (hygon_f18h_m4h()) - socket_id_mask = (ctx.tmp >> 16) & 0x7FF; - else - socket_id_mask = (ctx.tmp >> 16) & 0xFF; + socket_id_mask = (ctx.tmp >> 16) & 0x7FF; cs_id |= ((cs_fabric_id & socket_id_mask) >> socket_id_shift) << sock_id_bit; } @@ -3286,7 +3256,7 @@ static void decode_umc_error(int node_id, struct mce *m) else umc = err.channel; - if (umc_normaddr_to_sysaddr(m->addr, pvt->mc_node_id, umc, &sys_addr)) { + if (hygon_umc_normaddr_to_sysaddr(m->addr, pvt->mc_node_id, umc, &sys_addr)) { err.err_code = ERR_NORM_ADDR; goto log_error; } -- Gitee From 95df203459e763ec7036b119dbca4bbdb1eb21d3 Mon Sep 17 00:00:00 2001 From: Wenhui Fan Date: Tue, 19 May 2026 20:27:47 +0800 Subject: [PATCH 11/12] anolis: EDAC/amd64: Correct the address translation for hygon family 18h model 4h ANBZ: #35240 For Hygon family 18h model 4h processor, the normal address does not contain sub-channel bit, so it need to reserve the bit field for sub-channel and fill it in the address translating process. In the 3 channels interleaving scenario, the calculation of cs id is different from other scenarios. Hygon-SIG: commit none hygon anolis: EDAC/amd64: Correct the address translation for hygon family 18h model 4h Signed-off-by: Wenhui Fan --- drivers/edac/amd64_edac.c | 98 +++++++++++++++++++++++++++++++++------ 1 file changed, 85 insertions(+), 13 deletions(-) diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index 5996df6ccf28..8ff3e82df2c1 100644 --- a/drivers/edac/amd64_edac.c +++ b/drivers/edac/amd64_edac.c @@ -1151,14 +1151,15 @@ struct addr_ctx { u8 inst_id; }; -static int hygon_umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, u64 *sys_addr) +static int hygon_umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, + u8 sub_channel, u64 *sys_addr) { u64 dram_base_addr, dram_limit_addr, dram_hole_base; - - u16 die_id_mask, socket_id_mask, cs_id = 0; + u16 die_id_mask, socket_id_mask, dst_fabric_id, cs_id = 0; u8 die_id_shift, socket_id_shift; u8 intlv_num_dies, intlv_num_chan, intlv_num_sockets; u8 intlv_addr_sel, intlv_addr_bit; + u8 chan_addr_sel, chan_hash_enable, ddr5_enable, start_bit; u8 num_intlv_bits, hashed_bit; u8 lgcy_mmio_hole_en, base = 0; u8 cs_mask; @@ -1218,6 +1219,8 @@ static int hygon_umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, u64 *sy intlv_num_dies = (ctx.tmp >> 10) & 0x3; dram_limit_addr = ((ctx.tmp & GENMASK_ULL(31, 12)) << 16) | GENMASK_ULL(27, 0); + if (boot_cpu_data.x86_model == 0x4) + dst_fabric_id = ctx.tmp & GENMASK_ULL(9, 0); intlv_addr_bit = intlv_addr_sel + 8; @@ -1227,6 +1230,31 @@ static int hygon_umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, u64 *sy intlv_num_dies = ctx.tmp & 0x3; } + if (boot_cpu_data.x86_model == 0x4) { + if (df_indirect_read_instance(nid, 2, 0x48, umc, &ctx.tmp)) + goto out_err; + chan_addr_sel = (ctx.tmp >> 24) & 0x1; + chan_hash_enable = (ctx.tmp >> 23) & 0x1; + ddr5_enable = (ctx.tmp >> 19) & 0x1; + if (ddr5_enable) { + u64 low_addr, high_addr; + + if (chan_addr_sel) + start_bit = 8; + else + start_bit = 7; + + low_addr = ctx.ret_addr & GENMASK_ULL(start_bit - 1, 0); + /* + * Reserve the sub-channel bit field(ret_addr[start_bit]), + * and fill the sub-channel bit in the later channel hashing + * process. + */ + high_addr = (ctx.ret_addr & GENMASK_ULL(63, start_bit)) << 1; + ctx.ret_addr = high_addr | low_addr; + } + } + /* Re-use intlv_num_chan by setting it equal to log2(#channels) */ switch (intlv_num_chan) { case 0: intlv_num_chan = 0; break; @@ -1268,7 +1296,7 @@ static int hygon_umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, u64 *sy } if (num_intlv_bits > 0) { - u64 temp_addr_x, temp_addr_i, temp_addr_y; + u64 temp_addr_x, temp_addr_i, temp_addr_y, addr_mul3; u8 die_id_bit, sock_id_bit; u16 cs_fabric_id; @@ -1287,8 +1315,29 @@ static int hygon_umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, u64 *sy /* If interleaved over more than 1 channel: */ if (intlv_num_chan) { die_id_bit = intlv_num_chan; - cs_mask = (1 << die_id_bit) - 1; - cs_id = cs_fabric_id & cs_mask; + /* + * In the 3 channels interleaving scenario, the calculation + * of cs id is different from other scenarios. + */ + if (boot_cpu_data.x86_model == 0x4 && intlv_num_chan == 2) { + u8 cs_offset; + + cs_offset = (cs_fabric_id & 0x3) - (dst_fabric_id & 0x3); + if (cs_offset > 3) { + pr_err("%s: Invalid cs offset: 0x%x cs_fabric_id: 0x%x dst_fabric_id: 0x%x.\n", + __func__, cs_offset, cs_fabric_id, dst_fabric_id); + goto out_err; + } + temp_addr_x = (ctx.ret_addr & GENMASK_ULL(63, intlv_addr_bit)) >> + intlv_addr_bit; + temp_addr_y = ctx.ret_addr & GENMASK_ULL(intlv_addr_bit - 1, 0); + + addr_mul3 = temp_addr_x * 3 + cs_offset; + cs_id = addr_mul3 & GENMASK_ULL(intlv_num_chan - 1, 0); + } else { + cs_mask = (1 << die_id_bit) - 1; + cs_id = cs_fabric_id & cs_mask; + } } sock_id_bit = die_id_bit; @@ -1323,10 +1372,16 @@ static int hygon_umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, u64 *sy * bits there are. "intlv_addr_bit" tells us how many "Y" bits * there are (where "I" starts). */ - temp_addr_y = ctx.ret_addr & GENMASK_ULL(intlv_addr_bit - 1, 0); - temp_addr_i = (cs_id << intlv_addr_bit); - temp_addr_x = (ctx.ret_addr & GENMASK_ULL(63, intlv_addr_bit)) << num_intlv_bits; - ctx.ret_addr = temp_addr_x | temp_addr_i | temp_addr_y; + if (boot_cpu_data.x86_model == 0x4 && intlv_num_chan == 2) { + temp_addr_i = ((addr_mul3 >> intlv_num_chan) << intlv_num_chan) | cs_id; + ctx.ret_addr = temp_addr_y | (temp_addr_i << intlv_addr_bit); + } else { + temp_addr_y = ctx.ret_addr & GENMASK_ULL(intlv_addr_bit - 1, 0); + temp_addr_i = (cs_id << intlv_addr_bit); + temp_addr_x = (ctx.ret_addr & GENMASK_ULL(63, intlv_addr_bit)) << + num_intlv_bits; + ctx.ret_addr = temp_addr_x | temp_addr_i | temp_addr_y; + } } /* Add dram base address */ @@ -1356,6 +1411,21 @@ static int hygon_umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, u64 *sy ctx.ret_addr ^= BIT(intlv_addr_bit); } + /* The channel hashing process. */ + if (boot_cpu_data.x86_model == 0x4 && ddr5_enable) { + if (chan_hash_enable) { + hashed_bit = (ctx.ret_addr >> 12) ^ + (ctx.ret_addr >> 21) ^ + (ctx.ret_addr >> 30) ^ + sub_channel; + hashed_bit &= BIT(0); + ctx.ret_addr |= hashed_bit << start_bit; + + } else { + ctx.ret_addr |= sub_channel << start_bit; + } + } + /* Is calculated system address is above DRAM limit address? */ if (ctx.ret_addr > dram_limit_addr) goto out_err; @@ -3219,7 +3289,7 @@ static void decode_umc_error(int node_id, struct mce *m) struct atl_err a_err; struct err_info err; u64 sys_addr; - u8 umc; + u8 umc, sub_channel = 0; node_id = fixup_node_id(node_id, m); @@ -3251,12 +3321,14 @@ static void decode_umc_error(int node_id, struct mce *m) pvt->ops->get_err_info(m, &err); if (hygon_f18h_m4h() || hygon_f18h_m10h()) { + sub_channel = (m->ipid & BIT(13)) >> 13; if (boot_cpu_data.x86_model >= 0x6) - umc = (err.channel << 1) + ((m->ipid & BIT(13)) >> 13); + umc = (err.channel << 1) + sub_channel; else umc = err.channel; - if (hygon_umc_normaddr_to_sysaddr(m->addr, pvt->mc_node_id, umc, &sys_addr)) { + if (hygon_umc_normaddr_to_sysaddr(m->addr, pvt->mc_node_id, umc, + sub_channel, &sys_addr)) { err.err_code = ERR_NORM_ADDR; goto log_error; } -- Gitee From 71a5750ac1e4e097c2a5c201cfae49f32bb02a75 Mon Sep 17 00:00:00 2001 From: Wenhui Fan Date: Tue, 19 May 2026 20:30:04 +0800 Subject: [PATCH 12/12] anolis: EDAC/amd64: The width of hash value is 2 bits for Hygon family 18h model 6h processors ANBZ: #35240 It has 2 bits hash value when hash enabled for hygon family 18h model 6h. Hygon-SIG: commit none hygon anolis: EDAC/amd64: The width of hash value is 2 bits for Hygon family 18h model 6h processors Hygon-SIG: commit none hygon anolis: EDAC/amd64: The width of hash value is 2 bits for Hygon family 18h model 6h processors Signed-off-by: Wenhui Fan --- drivers/edac/amd64_edac.c | 21 +++++++++++++++------ 1 file changed, 15 insertions(+), 6 deletions(-) diff --git a/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index 8ff3e82df2c1..f1303d1ee4bc 100644 --- a/drivers/edac/amd64_edac.c +++ b/drivers/edac/amd64_edac.c @@ -1263,7 +1263,11 @@ static int hygon_umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, case 5: intlv_num_chan = 3; break; case 7: intlv_num_chan = 4; break; - case 8: intlv_num_chan = 1; + case 8: + if (boot_cpu_data.x86_model >= 0x6) + intlv_num_chan = 2; + else + intlv_num_chan = 1; hash_enabled = true; break; default: @@ -1398,17 +1402,22 @@ static int hygon_umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, } if (hash_enabled) { - /* Save some parentheses and grab ls-bit at the end. */ hashed_bit = (ctx.ret_addr >> 12) ^ (ctx.ret_addr >> 18) ^ (ctx.ret_addr >> 21) ^ (ctx.ret_addr >> 30) ^ cs_id; - hashed_bit &= BIT(0); - - if (hashed_bit != ((ctx.ret_addr >> intlv_addr_bit) & BIT(0))) - ctx.ret_addr ^= BIT(intlv_addr_bit); + if (boot_cpu_data.x86_model >= 0x6) { + hashed_bit &= 0x3; + if (hashed_bit != ((ctx.ret_addr >> intlv_addr_bit) & 0x3)) + ctx.ret_addr = (ctx.ret_addr & ~((u64)3 << intlv_addr_bit)) | + (hashed_bit << intlv_addr_bit); + } else { + hashed_bit &= BIT(0); + if (hashed_bit != ((ctx.ret_addr >> intlv_addr_bit) & BIT(0))) + ctx.ret_addr ^= BIT(intlv_addr_bit); + } } /* The channel hashing process. */ -- Gitee