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 d1e69f2645ee2c77de6b4898003ee1bcd80bc1e8..5a6c23dd5c279c00f365481838898affca86fbb8 100644 --- a/arch/x86/Kconfig +++ b/arch/x86/Kconfig @@ -3092,6 +3092,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 + config X86_SYSFB bool "Mark VGA/VBE/EFI FB as generic system framebuffer" help 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..9a791288af5460bd122a6a89a87610fa6b2d1748 --- /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(x) false +#define hygon_f18h_m10h(x) false +#define get_df_id(x, y) NULL +#define hygon_node_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 a19ce38df85d11db95522c832c4045b1497fe215..a3f4b0b007c72d5cd6d88dcd656bdb6b9c094545 100644 --- a/arch/x86/kernel/Makefile +++ b/arch/x86/kernel/Makefile @@ -119,6 +119,7 @@ obj-$(CONFIG_APB_TIMER) += apb_timer.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 9595b90e2bace6c9209a53c29868c67e6dac4b8e..9a4e6dd09d4ee8cbe2848c15111f3c4b00cf7e42 100644 --- a/arch/x86/kernel/amd_nb.c +++ b/arch/x86/kernel/amd_nb.c @@ -20,18 +20,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) }, @@ -45,33 +34,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 }, @@ -110,244 +72,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,13 +80,6 @@ static int amd_cache_northbridges(void) if (amd_northbridges.num) return 0; - if (boot_cpu_data.x86_vendor == X86_VENDOR_HYGON) { - if (boot_cpu_data.x86_model >= 0x4 && - boot_cpu_data.x86_model <= 0xf) - return northbridge_init_f18h_m4h(hygon_root_ids, - hygon_nb_misc_ids, hygon_nb_link_ids); - } - amd_northbridges.num = amd_num_nodes(); nb = kcalloc(amd_northbridges.num, sizeof(struct amd_northbridge), GFP_KERNEL); @@ -426,8 +143,7 @@ bool __init early_is_amd_nb(u32 device) const struct pci_device_id *id; u32 vendor = device & 0xffff; - if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD && - boot_cpu_data.x86_vendor != X86_VENDOR_HYGON) + if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD) return false; if (cpu_feature_enabled(X86_FEATURE_ZEN)) @@ -446,8 +162,7 @@ struct resource *amd_get_mmconfig_range(struct resource *res) u64 base, msr; unsigned int segn_busn_bits; - 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 NULL; /* assume all cpus from fam10h have mmconfig */ @@ -619,8 +334,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..fcb386e1eff84fc6bd81028f437903f2b89f3444 --- /dev/null +++ b/arch/x86/kernel/hygon_nb.c @@ -0,0 +1,415 @@ +// 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_M04H_ROOT 0x1480 +#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_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_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 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) }, + { 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_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) }, + { 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_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) }, + { 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) }, + {} +}; + +#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); + hygon_northbridges.nb = NULL; + hygon_northbridges.num = 0; +err_free_roots: + kfree(hygon_roots); + hygon_roots = NULL; + +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 7f7aaa2af18955eae6b25b469973ee818012ac99..d54c7841678007f28a3a76abd14106ce51779abf 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); @@ -753,10 +754,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; @@ -1507,7 +1508,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; @@ -1523,10 +1524,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; @@ -3011,6 +3072,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. */ @@ -3022,13 +3115,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]; @@ -3612,8 +3699,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; @@ -4148,7 +4241,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; @@ -4165,6 +4258,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); @@ -4228,11 +4325,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) @@ -4292,13 +4394,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(); @@ -4369,7 +4471,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 4caa988f36c24d75d5938096b99670836bd5a1a5..26e836ac44283f918423cb2b660783ba93166653 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 "); @@ -168,6 +169,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; @@ -211,21 +226,21 @@ static int k10temp_read_labels(struct device *dev, return 0; } -static void hygon_read_temp(struct k10temp_data *data, int channel, +static int hygon_read_temp(struct k10temp_data *data, int channel, u32 *regval) { struct hygon_private *h_priv; h_priv = (struct hygon_private *)data->priv; if ((channel - 2) < h_priv->index_2nd) - amd_smn_read(amd_pci_dev_to_node_id(data->pdev), - ZEN_CCD_TEMP(data->ccd_offset, channel - 2), - regval); + return hygon_smn_read(hygon_pci_dev_to_node_id(data->pdev), + ZEN_CCD_TEMP(data->ccd_offset, channel - 2), + regval); else - amd_smn_read(amd_pci_dev_to_node_id(data->pdev), - ZEN_CCD_TEMP(h_priv->offset_2nd, - channel - 2 - h_priv->index_2nd), - regval); + return 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); } static int k10temp_read_temp(struct device *dev, u32 attr, int channel, @@ -250,7 +265,9 @@ static int k10temp_read_temp(struct device *dev, u32 attr, int channel, break; case 2 ... 13: /* Tccd{1-12} */ if (hygon_f18h_m4h()) { - hygon_read_temp(data, channel, ®val); + ret = hygon_read_temp(data, channel, ®val); + if (ret) + return ret; } else { ret = amd_smn_read(amd_pci_dev_to_node_id(data->pdev), ZEN_CCD_TEMP(data->ccd_offset, channel - 2), @@ -426,9 +443,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)); @@ -444,10 +467,11 @@ 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++) { - 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)); } @@ -523,7 +547,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; switch (boot_cpu_data.x86_model) { @@ -614,7 +638,7 @@ static const struct pci_device_id k10temp_id_table[] = { { PCI_VDEVICE(AMD, PCI_DEVICE_ID_AMD_1AH_M50H_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 a78d5e0f50e572498fb868faf91d0ebf9e4c3fe5..fbe986b3e2cce5840bd0cbbe3880adf26c114a60 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -2610,6 +2610,8 @@ #define PCI_VENDOR_ID_HYGON 0x1d94 #define PCI_DEVICE_ID_HYGON_18H_M05H_HDA 0x14a9 +#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