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 0000000000000000000000000000000000000000..290c38414c64bc4fa78a9a34db67f12d5acebb04 --- /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 f22b398dd04f6cdfc634add3ae3ad51b63337469..848820cc8e0dd8397795b50735d63b828847e361 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 0000000000000000000000000000000000000000..7eeaf77dc98a1422b0ac0635e36f8efb60055733 --- /dev/null +++ b/arch/x86/include/asm/hygon/hygon_nb.h @@ -0,0 +1,63 @@ +/* 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; +}; + +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); +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); + +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 +#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) + +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 9ff1490bdd4ae461fafaab90956fa484fb530ac0..e3311bd0a089d3ed0016aedc5f189b20e37dd71f 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 1d9b2e5047cb4773a231183846c38b4f3dfb54fb..b0ce5296bf5f48dc4a1b5591bc8f871de81cff03 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 }, @@ -100,254 +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; -} - -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 +70,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 +322,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 0000000000000000000000000000000000000000..9acc4c37298a99eca913e8af7999f325539e7933 --- /dev/null +++ b/arch/x86/kernel/hygon_nb.c @@ -0,0 +1,401 @@ +// 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_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 +#define PCI_DEVICE_ID_HYGON_18H_M06H_DF_F5 0x14b5 + +static u16 node_num; +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) }, + { 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) }, + {} +}; + +#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) +{ + 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_node_num(void) +{ + return node_num; +} +EXPORT_SYMBOL_GPL(hygon_node_num); + +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); + +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 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); + +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; + 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); +} + +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; + u8 i, j, m, n; + u8 id; + + if (hygon_northbridges.num) + return 0; + + misc = NULL; + while ((misc = next_northbridge(misc, misc_ids))) + 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; + 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; + j = m = n = 0; + for (i = 0; i < hygon_northbridges.num; i++) { + 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); + +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/drivers/edac/amd64_edac.c b/drivers/edac/amd64_edac.c index d4d2d08d14f1724aab6aa6daed17f34a101a0ba3..f1303d1ee4bcb98718ead79ce59d8a0a6e7fae0e 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; @@ -1150,21 +1151,18 @@ 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, + u8 sub_channel, u64 *sys_addr) { u64 dram_base_addr, dram_limit_addr, dram_hole_base; - + u16 die_id_mask, socket_id_mask, dst_fabric_id, 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 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, cs_id = 0; + u8 cs_mask; bool hash_enabled = false; struct addr_ctx ctx; @@ -1178,10 +1176,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: */ @@ -1205,9 +1200,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; @@ -1224,20 +1217,44 @@ 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); + if (boot_cpu_data.x86_model == 0x4) + dst_fabric_id = ctx.tmp & GENMASK_ULL(9, 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; } + 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; @@ -1246,11 +1263,15 @@ static int umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, u64 *sys_addr 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: - 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", @@ -1272,21 +1293,16 @@ 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; } 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; -#ifdef CONFIG_CPU_SUP_HYGON u16 cs_fabric_id; -#else - u8 cs_fabric_id; -#endif /* * Read FabricBlockInstanceInformation3_CS[BlockFabricID]. @@ -1297,17 +1313,35 @@ 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: */ 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; @@ -1320,26 +1354,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; } @@ -1352,10 +1376,16 @@ static int umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, u64 *sys_addr * 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 */ @@ -1372,17 +1402,37 @@ static int umc_normaddr_to_sysaddr(u64 norm_addr, u16 nid, u8 umc, u64 *sys_addr } 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 (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. */ + 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; - if (hashed_bit != ((ctx.ret_addr >> intlv_addr_bit) & BIT(0))) - ctx.ret_addr ^= BIT(intlv_addr_bit); + } else { + ctx.ret_addr |= sub_channel << start_bit; + } } /* Is calculated system address is above DRAM limit address? */ @@ -1803,7 +1853,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 +1869,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; @@ -3188,7 +3298,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); @@ -3220,12 +3330,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 (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; } @@ -3299,6 +3411,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 +3454,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 +4026,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 +4573,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 +4590,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 +4657,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 +4729,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 +4806,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(); diff --git a/drivers/hwmon/k10temp.c b/drivers/hwmon/k10temp.c index f7fe331102741cbf0076ef783ca63c18454d3241..e5ae2c2e8e25461e2a8854f642e1c731cb30198f 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) }, diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 2dc035374f79d449e0491d6e491835145628aea7..397ae63d5f8f680359fcbbdcb3a8777e6d5c4cb6 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2604,6 +2604,8 @@ #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_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