162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Shared support code for AMD K8 northbridges and derivatives. 462306a36Sopenharmony_ci * Copyright 2006 Andi Kleen, SUSE Labs. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/types.h> 1062306a36Sopenharmony_ci#include <linux/slab.h> 1162306a36Sopenharmony_ci#include <linux/init.h> 1262306a36Sopenharmony_ci#include <linux/errno.h> 1362306a36Sopenharmony_ci#include <linux/export.h> 1462306a36Sopenharmony_ci#include <linux/spinlock.h> 1562306a36Sopenharmony_ci#include <linux/pci_ids.h> 1662306a36Sopenharmony_ci#include <asm/amd_nb.h> 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define PCI_DEVICE_ID_AMD_17H_ROOT 0x1450 1962306a36Sopenharmony_ci#define PCI_DEVICE_ID_AMD_17H_M10H_ROOT 0x15d0 2062306a36Sopenharmony_ci#define PCI_DEVICE_ID_AMD_17H_M30H_ROOT 0x1480 2162306a36Sopenharmony_ci#define PCI_DEVICE_ID_AMD_17H_M60H_ROOT 0x1630 2262306a36Sopenharmony_ci#define PCI_DEVICE_ID_AMD_17H_MA0H_ROOT 0x14b5 2362306a36Sopenharmony_ci#define PCI_DEVICE_ID_AMD_19H_M10H_ROOT 0x14a4 2462306a36Sopenharmony_ci#define PCI_DEVICE_ID_AMD_19H_M40H_ROOT 0x14b5 2562306a36Sopenharmony_ci#define PCI_DEVICE_ID_AMD_19H_M60H_ROOT 0x14d8 2662306a36Sopenharmony_ci#define PCI_DEVICE_ID_AMD_19H_M70H_ROOT 0x14e8 2762306a36Sopenharmony_ci#define PCI_DEVICE_ID_AMD_1AH_M00H_ROOT 0x153a 2862306a36Sopenharmony_ci#define PCI_DEVICE_ID_AMD_1AH_M20H_ROOT 0x1507 2962306a36Sopenharmony_ci#define PCI_DEVICE_ID_AMD_MI200_ROOT 0x14bb 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ci#define PCI_DEVICE_ID_AMD_17H_DF_F4 0x1464 3262306a36Sopenharmony_ci#define PCI_DEVICE_ID_AMD_17H_M10H_DF_F4 0x15ec 3362306a36Sopenharmony_ci#define PCI_DEVICE_ID_AMD_17H_M30H_DF_F4 0x1494 3462306a36Sopenharmony_ci#define PCI_DEVICE_ID_AMD_17H_M60H_DF_F4 0x144c 3562306a36Sopenharmony_ci#define PCI_DEVICE_ID_AMD_17H_M70H_DF_F4 0x1444 3662306a36Sopenharmony_ci#define PCI_DEVICE_ID_AMD_17H_MA0H_DF_F4 0x1728 3762306a36Sopenharmony_ci#define PCI_DEVICE_ID_AMD_19H_DF_F4 0x1654 3862306a36Sopenharmony_ci#define PCI_DEVICE_ID_AMD_19H_M10H_DF_F4 0x14b1 3962306a36Sopenharmony_ci#define PCI_DEVICE_ID_AMD_19H_M40H_DF_F4 0x167d 4062306a36Sopenharmony_ci#define PCI_DEVICE_ID_AMD_19H_M50H_DF_F4 0x166e 4162306a36Sopenharmony_ci#define PCI_DEVICE_ID_AMD_19H_M60H_DF_F4 0x14e4 4262306a36Sopenharmony_ci#define PCI_DEVICE_ID_AMD_19H_M70H_DF_F4 0x14f4 4362306a36Sopenharmony_ci#define PCI_DEVICE_ID_AMD_19H_M78H_DF_F4 0x12fc 4462306a36Sopenharmony_ci#define PCI_DEVICE_ID_AMD_1AH_M00H_DF_F4 0x12c4 4562306a36Sopenharmony_ci#define PCI_DEVICE_ID_AMD_MI200_DF_F4 0x14d4 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* Protect the PCI config register pairs used for SMN. */ 4862306a36Sopenharmony_cistatic DEFINE_MUTEX(smn_mutex); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_cistatic u32 *flush_words; 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_cistatic const struct pci_device_id amd_root_ids[] = { 5362306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_ROOT) }, 5462306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_M10H_ROOT) }, 5562306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_M30H_ROOT) }, 5662306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_M60H_ROOT) }, 5762306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_MA0H_ROOT) }, 5862306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_19H_M10H_ROOT) }, 5962306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_19H_M40H_ROOT) }, 6062306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_19H_M60H_ROOT) }, 6162306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_19H_M70H_ROOT) }, 6262306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_1AH_M00H_ROOT) }, 6362306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_1AH_M20H_ROOT) }, 6462306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_MI200_ROOT) }, 6562306a36Sopenharmony_ci {} 6662306a36Sopenharmony_ci}; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci#define PCI_DEVICE_ID_AMD_CNB17H_F4 0x1704 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_cistatic const struct pci_device_id amd_nb_misc_ids[] = { 7162306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_K8_NB_MISC) }, 7262306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_10H_NB_MISC) }, 7362306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_15H_NB_F3) }, 7462306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_15H_M10H_F3) }, 7562306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_15H_M30H_NB_F3) }, 7662306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_15H_M60H_NB_F3) }, 7762306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_16H_NB_F3) }, 7862306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_16H_M30H_NB_F3) }, 7962306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_DF_F3) }, 8062306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_M10H_DF_F3) }, 8162306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_M30H_DF_F3) }, 8262306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_M60H_DF_F3) }, 8362306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_MA0H_DF_F3) }, 8462306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CNB17H_F3) }, 8562306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_M70H_DF_F3) }, 8662306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_19H_DF_F3) }, 8762306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_19H_M10H_DF_F3) }, 8862306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_19H_M40H_DF_F3) }, 8962306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_19H_M50H_DF_F3) }, 9062306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_19H_M60H_DF_F3) }, 9162306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_19H_M70H_DF_F3) }, 9262306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_19H_M78H_DF_F3) }, 9362306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_1AH_M00H_DF_F3) }, 9462306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_1AH_M20H_DF_F3) }, 9562306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_MI200_DF_F3) }, 9662306a36Sopenharmony_ci {} 9762306a36Sopenharmony_ci}; 9862306a36Sopenharmony_ci 9962306a36Sopenharmony_cistatic const struct pci_device_id amd_nb_link_ids[] = { 10062306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_15H_NB_F4) }, 10162306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_15H_M30H_NB_F4) }, 10262306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_15H_M60H_NB_F4) }, 10362306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_16H_NB_F4) }, 10462306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_16H_M30H_NB_F4) }, 10562306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_DF_F4) }, 10662306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_M10H_DF_F4) }, 10762306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_M30H_DF_F4) }, 10862306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_M60H_DF_F4) }, 10962306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_M70H_DF_F4) }, 11062306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_17H_MA0H_DF_F4) }, 11162306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_19H_DF_F4) }, 11262306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_19H_M10H_DF_F4) }, 11362306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_19H_M40H_DF_F4) }, 11462306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_19H_M50H_DF_F4) }, 11562306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_19H_M60H_DF_F4) }, 11662306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_19H_M70H_DF_F4) }, 11762306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_19H_M78H_DF_F4) }, 11862306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_CNB17H_F4) }, 11962306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_1AH_M00H_DF_F4) }, 12062306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_MI200_DF_F4) }, 12162306a36Sopenharmony_ci {} 12262306a36Sopenharmony_ci}; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistatic const struct pci_device_id hygon_root_ids[] = { 12562306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_HYGON, PCI_DEVICE_ID_AMD_17H_ROOT) }, 12662306a36Sopenharmony_ci {} 12762306a36Sopenharmony_ci}; 12862306a36Sopenharmony_ci 12962306a36Sopenharmony_cistatic const struct pci_device_id hygon_nb_misc_ids[] = { 13062306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_HYGON, PCI_DEVICE_ID_AMD_17H_DF_F3) }, 13162306a36Sopenharmony_ci {} 13262306a36Sopenharmony_ci}; 13362306a36Sopenharmony_ci 13462306a36Sopenharmony_cistatic const struct pci_device_id hygon_nb_link_ids[] = { 13562306a36Sopenharmony_ci { PCI_DEVICE(PCI_VENDOR_ID_HYGON, PCI_DEVICE_ID_AMD_17H_DF_F4) }, 13662306a36Sopenharmony_ci {} 13762306a36Sopenharmony_ci}; 13862306a36Sopenharmony_ci 13962306a36Sopenharmony_ciconst struct amd_nb_bus_dev_range amd_nb_bus_dev_ranges[] __initconst = { 14062306a36Sopenharmony_ci { 0x00, 0x18, 0x20 }, 14162306a36Sopenharmony_ci { 0xff, 0x00, 0x20 }, 14262306a36Sopenharmony_ci { 0xfe, 0x00, 0x20 }, 14362306a36Sopenharmony_ci { } 14462306a36Sopenharmony_ci}; 14562306a36Sopenharmony_ci 14662306a36Sopenharmony_cistatic struct amd_northbridge_info amd_northbridges; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ciu16 amd_nb_num(void) 14962306a36Sopenharmony_ci{ 15062306a36Sopenharmony_ci return amd_northbridges.num; 15162306a36Sopenharmony_ci} 15262306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(amd_nb_num); 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_cibool amd_nb_has_feature(unsigned int feature) 15562306a36Sopenharmony_ci{ 15662306a36Sopenharmony_ci return ((amd_northbridges.flags & feature) == feature); 15762306a36Sopenharmony_ci} 15862306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(amd_nb_has_feature); 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_cistruct amd_northbridge *node_to_amd_nb(int node) 16162306a36Sopenharmony_ci{ 16262306a36Sopenharmony_ci return (node < amd_northbridges.num) ? &amd_northbridges.nb[node] : NULL; 16362306a36Sopenharmony_ci} 16462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(node_to_amd_nb); 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic struct pci_dev *next_northbridge(struct pci_dev *dev, 16762306a36Sopenharmony_ci const struct pci_device_id *ids) 16862306a36Sopenharmony_ci{ 16962306a36Sopenharmony_ci do { 17062306a36Sopenharmony_ci dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev); 17162306a36Sopenharmony_ci if (!dev) 17262306a36Sopenharmony_ci break; 17362306a36Sopenharmony_ci } while (!pci_match_id(ids, dev)); 17462306a36Sopenharmony_ci return dev; 17562306a36Sopenharmony_ci} 17662306a36Sopenharmony_ci 17762306a36Sopenharmony_cistatic int __amd_smn_rw(u16 node, u32 address, u32 *value, bool write) 17862306a36Sopenharmony_ci{ 17962306a36Sopenharmony_ci struct pci_dev *root; 18062306a36Sopenharmony_ci int err = -ENODEV; 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci if (node >= amd_northbridges.num) 18362306a36Sopenharmony_ci goto out; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_ci root = node_to_amd_nb(node)->root; 18662306a36Sopenharmony_ci if (!root) 18762306a36Sopenharmony_ci goto out; 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci mutex_lock(&smn_mutex); 19062306a36Sopenharmony_ci 19162306a36Sopenharmony_ci err = pci_write_config_dword(root, 0x60, address); 19262306a36Sopenharmony_ci if (err) { 19362306a36Sopenharmony_ci pr_warn("Error programming SMN address 0x%x.\n", address); 19462306a36Sopenharmony_ci goto out_unlock; 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci err = (write ? pci_write_config_dword(root, 0x64, *value) 19862306a36Sopenharmony_ci : pci_read_config_dword(root, 0x64, value)); 19962306a36Sopenharmony_ci if (err) 20062306a36Sopenharmony_ci pr_warn("Error %s SMN address 0x%x.\n", 20162306a36Sopenharmony_ci (write ? "writing to" : "reading from"), address); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ciout_unlock: 20462306a36Sopenharmony_ci mutex_unlock(&smn_mutex); 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_ciout: 20762306a36Sopenharmony_ci return err; 20862306a36Sopenharmony_ci} 20962306a36Sopenharmony_ci 21062306a36Sopenharmony_ciint amd_smn_read(u16 node, u32 address, u32 *value) 21162306a36Sopenharmony_ci{ 21262306a36Sopenharmony_ci return __amd_smn_rw(node, address, value, false); 21362306a36Sopenharmony_ci} 21462306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(amd_smn_read); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ciint amd_smn_write(u16 node, u32 address, u32 value) 21762306a36Sopenharmony_ci{ 21862306a36Sopenharmony_ci return __amd_smn_rw(node, address, &value, true); 21962306a36Sopenharmony_ci} 22062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(amd_smn_write); 22162306a36Sopenharmony_ci 22262306a36Sopenharmony_ci 22362306a36Sopenharmony_cistatic int amd_cache_northbridges(void) 22462306a36Sopenharmony_ci{ 22562306a36Sopenharmony_ci const struct pci_device_id *misc_ids = amd_nb_misc_ids; 22662306a36Sopenharmony_ci const struct pci_device_id *link_ids = amd_nb_link_ids; 22762306a36Sopenharmony_ci const struct pci_device_id *root_ids = amd_root_ids; 22862306a36Sopenharmony_ci struct pci_dev *root, *misc, *link; 22962306a36Sopenharmony_ci struct amd_northbridge *nb; 23062306a36Sopenharmony_ci u16 roots_per_misc = 0; 23162306a36Sopenharmony_ci u16 misc_count = 0; 23262306a36Sopenharmony_ci u16 root_count = 0; 23362306a36Sopenharmony_ci u16 i, j; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci if (amd_northbridges.num) 23662306a36Sopenharmony_ci return 0; 23762306a36Sopenharmony_ci 23862306a36Sopenharmony_ci if (boot_cpu_data.x86_vendor == X86_VENDOR_HYGON) { 23962306a36Sopenharmony_ci root_ids = hygon_root_ids; 24062306a36Sopenharmony_ci misc_ids = hygon_nb_misc_ids; 24162306a36Sopenharmony_ci link_ids = hygon_nb_link_ids; 24262306a36Sopenharmony_ci } 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci misc = NULL; 24562306a36Sopenharmony_ci while ((misc = next_northbridge(misc, misc_ids))) 24662306a36Sopenharmony_ci misc_count++; 24762306a36Sopenharmony_ci 24862306a36Sopenharmony_ci if (!misc_count) 24962306a36Sopenharmony_ci return -ENODEV; 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci root = NULL; 25262306a36Sopenharmony_ci while ((root = next_northbridge(root, root_ids))) 25362306a36Sopenharmony_ci root_count++; 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_ci if (root_count) { 25662306a36Sopenharmony_ci roots_per_misc = root_count / misc_count; 25762306a36Sopenharmony_ci 25862306a36Sopenharmony_ci /* 25962306a36Sopenharmony_ci * There should be _exactly_ N roots for each DF/SMN 26062306a36Sopenharmony_ci * interface. 26162306a36Sopenharmony_ci */ 26262306a36Sopenharmony_ci if (!roots_per_misc || (root_count % roots_per_misc)) { 26362306a36Sopenharmony_ci pr_info("Unsupported AMD DF/PCI configuration found\n"); 26462306a36Sopenharmony_ci return -ENODEV; 26562306a36Sopenharmony_ci } 26662306a36Sopenharmony_ci } 26762306a36Sopenharmony_ci 26862306a36Sopenharmony_ci nb = kcalloc(misc_count, sizeof(struct amd_northbridge), GFP_KERNEL); 26962306a36Sopenharmony_ci if (!nb) 27062306a36Sopenharmony_ci return -ENOMEM; 27162306a36Sopenharmony_ci 27262306a36Sopenharmony_ci amd_northbridges.nb = nb; 27362306a36Sopenharmony_ci amd_northbridges.num = misc_count; 27462306a36Sopenharmony_ci 27562306a36Sopenharmony_ci link = misc = root = NULL; 27662306a36Sopenharmony_ci for (i = 0; i < amd_northbridges.num; i++) { 27762306a36Sopenharmony_ci node_to_amd_nb(i)->root = root = 27862306a36Sopenharmony_ci next_northbridge(root, root_ids); 27962306a36Sopenharmony_ci node_to_amd_nb(i)->misc = misc = 28062306a36Sopenharmony_ci next_northbridge(misc, misc_ids); 28162306a36Sopenharmony_ci node_to_amd_nb(i)->link = link = 28262306a36Sopenharmony_ci next_northbridge(link, link_ids); 28362306a36Sopenharmony_ci 28462306a36Sopenharmony_ci /* 28562306a36Sopenharmony_ci * If there are more PCI root devices than data fabric/ 28662306a36Sopenharmony_ci * system management network interfaces, then the (N) 28762306a36Sopenharmony_ci * PCI roots per DF/SMN interface are functionally the 28862306a36Sopenharmony_ci * same (for DF/SMN access) and N-1 are redundant. N-1 28962306a36Sopenharmony_ci * PCI roots should be skipped per DF/SMN interface so 29062306a36Sopenharmony_ci * the following DF/SMN interfaces get mapped to 29162306a36Sopenharmony_ci * correct PCI roots. 29262306a36Sopenharmony_ci */ 29362306a36Sopenharmony_ci for (j = 1; j < roots_per_misc; j++) 29462306a36Sopenharmony_ci root = next_northbridge(root, root_ids); 29562306a36Sopenharmony_ci } 29662306a36Sopenharmony_ci 29762306a36Sopenharmony_ci if (amd_gart_present()) 29862306a36Sopenharmony_ci amd_northbridges.flags |= AMD_NB_GART; 29962306a36Sopenharmony_ci 30062306a36Sopenharmony_ci /* 30162306a36Sopenharmony_ci * Check for L3 cache presence. 30262306a36Sopenharmony_ci */ 30362306a36Sopenharmony_ci if (!cpuid_edx(0x80000006)) 30462306a36Sopenharmony_ci return 0; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci /* 30762306a36Sopenharmony_ci * Some CPU families support L3 Cache Index Disable. There are some 30862306a36Sopenharmony_ci * limitations because of E382 and E388 on family 0x10. 30962306a36Sopenharmony_ci */ 31062306a36Sopenharmony_ci if (boot_cpu_data.x86 == 0x10 && 31162306a36Sopenharmony_ci boot_cpu_data.x86_model >= 0x8 && 31262306a36Sopenharmony_ci (boot_cpu_data.x86_model > 0x9 || 31362306a36Sopenharmony_ci boot_cpu_data.x86_stepping >= 0x1)) 31462306a36Sopenharmony_ci amd_northbridges.flags |= AMD_NB_L3_INDEX_DISABLE; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci if (boot_cpu_data.x86 == 0x15) 31762306a36Sopenharmony_ci amd_northbridges.flags |= AMD_NB_L3_INDEX_DISABLE; 31862306a36Sopenharmony_ci 31962306a36Sopenharmony_ci /* L3 cache partitioning is supported on family 0x15 */ 32062306a36Sopenharmony_ci if (boot_cpu_data.x86 == 0x15) 32162306a36Sopenharmony_ci amd_northbridges.flags |= AMD_NB_L3_PARTITIONING; 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci return 0; 32462306a36Sopenharmony_ci} 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci/* 32762306a36Sopenharmony_ci * Ignores subdevice/subvendor but as far as I can figure out 32862306a36Sopenharmony_ci * they're useless anyways 32962306a36Sopenharmony_ci */ 33062306a36Sopenharmony_cibool __init early_is_amd_nb(u32 device) 33162306a36Sopenharmony_ci{ 33262306a36Sopenharmony_ci const struct pci_device_id *misc_ids = amd_nb_misc_ids; 33362306a36Sopenharmony_ci const struct pci_device_id *id; 33462306a36Sopenharmony_ci u32 vendor = device & 0xffff; 33562306a36Sopenharmony_ci 33662306a36Sopenharmony_ci if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD && 33762306a36Sopenharmony_ci boot_cpu_data.x86_vendor != X86_VENDOR_HYGON) 33862306a36Sopenharmony_ci return false; 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_ci if (boot_cpu_data.x86_vendor == X86_VENDOR_HYGON) 34162306a36Sopenharmony_ci misc_ids = hygon_nb_misc_ids; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_ci device >>= 16; 34462306a36Sopenharmony_ci for (id = misc_ids; id->vendor; id++) 34562306a36Sopenharmony_ci if (vendor == id->vendor && device == id->device) 34662306a36Sopenharmony_ci return true; 34762306a36Sopenharmony_ci return false; 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_cistruct resource *amd_get_mmconfig_range(struct resource *res) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci u32 address; 35362306a36Sopenharmony_ci u64 base, msr; 35462306a36Sopenharmony_ci unsigned int segn_busn_bits; 35562306a36Sopenharmony_ci 35662306a36Sopenharmony_ci if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD && 35762306a36Sopenharmony_ci boot_cpu_data.x86_vendor != X86_VENDOR_HYGON) 35862306a36Sopenharmony_ci return NULL; 35962306a36Sopenharmony_ci 36062306a36Sopenharmony_ci /* assume all cpus from fam10h have mmconfig */ 36162306a36Sopenharmony_ci if (boot_cpu_data.x86 < 0x10) 36262306a36Sopenharmony_ci return NULL; 36362306a36Sopenharmony_ci 36462306a36Sopenharmony_ci address = MSR_FAM10H_MMIO_CONF_BASE; 36562306a36Sopenharmony_ci rdmsrl(address, msr); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci /* mmconfig is not enabled */ 36862306a36Sopenharmony_ci if (!(msr & FAM10H_MMIO_CONF_ENABLE)) 36962306a36Sopenharmony_ci return NULL; 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_ci base = msr & (FAM10H_MMIO_CONF_BASE_MASK<<FAM10H_MMIO_CONF_BASE_SHIFT); 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci segn_busn_bits = (msr >> FAM10H_MMIO_CONF_BUSRANGE_SHIFT) & 37462306a36Sopenharmony_ci FAM10H_MMIO_CONF_BUSRANGE_MASK; 37562306a36Sopenharmony_ci 37662306a36Sopenharmony_ci res->flags = IORESOURCE_MEM; 37762306a36Sopenharmony_ci res->start = base; 37862306a36Sopenharmony_ci res->end = base + (1ULL<<(segn_busn_bits + 20)) - 1; 37962306a36Sopenharmony_ci return res; 38062306a36Sopenharmony_ci} 38162306a36Sopenharmony_ci 38262306a36Sopenharmony_ciint amd_get_subcaches(int cpu) 38362306a36Sopenharmony_ci{ 38462306a36Sopenharmony_ci struct pci_dev *link = node_to_amd_nb(topology_die_id(cpu))->link; 38562306a36Sopenharmony_ci unsigned int mask; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci if (!amd_nb_has_feature(AMD_NB_L3_PARTITIONING)) 38862306a36Sopenharmony_ci return 0; 38962306a36Sopenharmony_ci 39062306a36Sopenharmony_ci pci_read_config_dword(link, 0x1d4, &mask); 39162306a36Sopenharmony_ci 39262306a36Sopenharmony_ci return (mask >> (4 * cpu_data(cpu).cpu_core_id)) & 0xf; 39362306a36Sopenharmony_ci} 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ciint amd_set_subcaches(int cpu, unsigned long mask) 39662306a36Sopenharmony_ci{ 39762306a36Sopenharmony_ci static unsigned int reset, ban; 39862306a36Sopenharmony_ci struct amd_northbridge *nb = node_to_amd_nb(topology_die_id(cpu)); 39962306a36Sopenharmony_ci unsigned int reg; 40062306a36Sopenharmony_ci int cuid; 40162306a36Sopenharmony_ci 40262306a36Sopenharmony_ci if (!amd_nb_has_feature(AMD_NB_L3_PARTITIONING) || mask > 0xf) 40362306a36Sopenharmony_ci return -EINVAL; 40462306a36Sopenharmony_ci 40562306a36Sopenharmony_ci /* if necessary, collect reset state of L3 partitioning and BAN mode */ 40662306a36Sopenharmony_ci if (reset == 0) { 40762306a36Sopenharmony_ci pci_read_config_dword(nb->link, 0x1d4, &reset); 40862306a36Sopenharmony_ci pci_read_config_dword(nb->misc, 0x1b8, &ban); 40962306a36Sopenharmony_ci ban &= 0x180000; 41062306a36Sopenharmony_ci } 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_ci /* deactivate BAN mode if any subcaches are to be disabled */ 41362306a36Sopenharmony_ci if (mask != 0xf) { 41462306a36Sopenharmony_ci pci_read_config_dword(nb->misc, 0x1b8, ®); 41562306a36Sopenharmony_ci pci_write_config_dword(nb->misc, 0x1b8, reg & ~0x180000); 41662306a36Sopenharmony_ci } 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_ci cuid = cpu_data(cpu).cpu_core_id; 41962306a36Sopenharmony_ci mask <<= 4 * cuid; 42062306a36Sopenharmony_ci mask |= (0xf ^ (1 << cuid)) << 26; 42162306a36Sopenharmony_ci 42262306a36Sopenharmony_ci pci_write_config_dword(nb->link, 0x1d4, mask); 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci /* reset BAN mode if L3 partitioning returned to reset state */ 42562306a36Sopenharmony_ci pci_read_config_dword(nb->link, 0x1d4, ®); 42662306a36Sopenharmony_ci if (reg == reset) { 42762306a36Sopenharmony_ci pci_read_config_dword(nb->misc, 0x1b8, ®); 42862306a36Sopenharmony_ci reg &= ~0x180000; 42962306a36Sopenharmony_ci pci_write_config_dword(nb->misc, 0x1b8, reg | ban); 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci return 0; 43362306a36Sopenharmony_ci} 43462306a36Sopenharmony_ci 43562306a36Sopenharmony_cistatic void amd_cache_gart(void) 43662306a36Sopenharmony_ci{ 43762306a36Sopenharmony_ci u16 i; 43862306a36Sopenharmony_ci 43962306a36Sopenharmony_ci if (!amd_nb_has_feature(AMD_NB_GART)) 44062306a36Sopenharmony_ci return; 44162306a36Sopenharmony_ci 44262306a36Sopenharmony_ci flush_words = kmalloc_array(amd_northbridges.num, sizeof(u32), GFP_KERNEL); 44362306a36Sopenharmony_ci if (!flush_words) { 44462306a36Sopenharmony_ci amd_northbridges.flags &= ~AMD_NB_GART; 44562306a36Sopenharmony_ci pr_notice("Cannot initialize GART flush words, GART support disabled\n"); 44662306a36Sopenharmony_ci return; 44762306a36Sopenharmony_ci } 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci for (i = 0; i != amd_northbridges.num; i++) 45062306a36Sopenharmony_ci pci_read_config_dword(node_to_amd_nb(i)->misc, 0x9c, &flush_words[i]); 45162306a36Sopenharmony_ci} 45262306a36Sopenharmony_ci 45362306a36Sopenharmony_civoid amd_flush_garts(void) 45462306a36Sopenharmony_ci{ 45562306a36Sopenharmony_ci int flushed, i; 45662306a36Sopenharmony_ci unsigned long flags; 45762306a36Sopenharmony_ci static DEFINE_SPINLOCK(gart_lock); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci if (!amd_nb_has_feature(AMD_NB_GART)) 46062306a36Sopenharmony_ci return; 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_ci /* 46362306a36Sopenharmony_ci * Avoid races between AGP and IOMMU. In theory it's not needed 46462306a36Sopenharmony_ci * but I'm not sure if the hardware won't lose flush requests 46562306a36Sopenharmony_ci * when another is pending. This whole thing is so expensive anyways 46662306a36Sopenharmony_ci * that it doesn't matter to serialize more. -AK 46762306a36Sopenharmony_ci */ 46862306a36Sopenharmony_ci spin_lock_irqsave(&gart_lock, flags); 46962306a36Sopenharmony_ci flushed = 0; 47062306a36Sopenharmony_ci for (i = 0; i < amd_northbridges.num; i++) { 47162306a36Sopenharmony_ci pci_write_config_dword(node_to_amd_nb(i)->misc, 0x9c, 47262306a36Sopenharmony_ci flush_words[i] | 1); 47362306a36Sopenharmony_ci flushed++; 47462306a36Sopenharmony_ci } 47562306a36Sopenharmony_ci for (i = 0; i < amd_northbridges.num; i++) { 47662306a36Sopenharmony_ci u32 w; 47762306a36Sopenharmony_ci /* Make sure the hardware actually executed the flush*/ 47862306a36Sopenharmony_ci for (;;) { 47962306a36Sopenharmony_ci pci_read_config_dword(node_to_amd_nb(i)->misc, 48062306a36Sopenharmony_ci 0x9c, &w); 48162306a36Sopenharmony_ci if (!(w & 1)) 48262306a36Sopenharmony_ci break; 48362306a36Sopenharmony_ci cpu_relax(); 48462306a36Sopenharmony_ci } 48562306a36Sopenharmony_ci } 48662306a36Sopenharmony_ci spin_unlock_irqrestore(&gart_lock, flags); 48762306a36Sopenharmony_ci if (!flushed) 48862306a36Sopenharmony_ci pr_notice("nothing to flush?\n"); 48962306a36Sopenharmony_ci} 49062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(amd_flush_garts); 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_cistatic void __fix_erratum_688(void *info) 49362306a36Sopenharmony_ci{ 49462306a36Sopenharmony_ci#define MSR_AMD64_IC_CFG 0xC0011021 49562306a36Sopenharmony_ci 49662306a36Sopenharmony_ci msr_set_bit(MSR_AMD64_IC_CFG, 3); 49762306a36Sopenharmony_ci msr_set_bit(MSR_AMD64_IC_CFG, 14); 49862306a36Sopenharmony_ci} 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci/* Apply erratum 688 fix so machines without a BIOS fix work. */ 50162306a36Sopenharmony_cistatic __init void fix_erratum_688(void) 50262306a36Sopenharmony_ci{ 50362306a36Sopenharmony_ci struct pci_dev *F4; 50462306a36Sopenharmony_ci u32 val; 50562306a36Sopenharmony_ci 50662306a36Sopenharmony_ci if (boot_cpu_data.x86 != 0x14) 50762306a36Sopenharmony_ci return; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci if (!amd_northbridges.num) 51062306a36Sopenharmony_ci return; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci F4 = node_to_amd_nb(0)->link; 51362306a36Sopenharmony_ci if (!F4) 51462306a36Sopenharmony_ci return; 51562306a36Sopenharmony_ci 51662306a36Sopenharmony_ci if (pci_read_config_dword(F4, 0x164, &val)) 51762306a36Sopenharmony_ci return; 51862306a36Sopenharmony_ci 51962306a36Sopenharmony_ci if (val & BIT(2)) 52062306a36Sopenharmony_ci return; 52162306a36Sopenharmony_ci 52262306a36Sopenharmony_ci on_each_cpu(__fix_erratum_688, NULL, 0); 52362306a36Sopenharmony_ci 52462306a36Sopenharmony_ci pr_info("x86/cpu/AMD: CPU erratum 688 worked around\n"); 52562306a36Sopenharmony_ci} 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_cistatic __init int init_amd_nbs(void) 52862306a36Sopenharmony_ci{ 52962306a36Sopenharmony_ci amd_cache_northbridges(); 53062306a36Sopenharmony_ci amd_cache_gart(); 53162306a36Sopenharmony_ci 53262306a36Sopenharmony_ci fix_erratum_688(); 53362306a36Sopenharmony_ci 53462306a36Sopenharmony_ci return 0; 53562306a36Sopenharmony_ci} 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci/* This has to go after the PCI subsystem */ 53862306a36Sopenharmony_cifs_initcall(init_amd_nbs); 539