162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci#include <linux/init.h> 362306a36Sopenharmony_ci#include <linux/pci.h> 462306a36Sopenharmony_ci#include <linux/topology.h> 562306a36Sopenharmony_ci#include <linux/cpu.h> 662306a36Sopenharmony_ci#include <linux/range.h> 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <asm/amd_nb.h> 962306a36Sopenharmony_ci#include <asm/pci_x86.h> 1062306a36Sopenharmony_ci 1162306a36Sopenharmony_ci#include <asm/pci-direct.h> 1262306a36Sopenharmony_ci 1362306a36Sopenharmony_ci#include "bus_numa.h" 1462306a36Sopenharmony_ci 1562306a36Sopenharmony_ci#define AMD_NB_F0_NODE_ID 0x60 1662306a36Sopenharmony_ci#define AMD_NB_F0_UNIT_ID 0x64 1762306a36Sopenharmony_ci#define AMD_NB_F1_CONFIG_MAP_REG 0xe0 1862306a36Sopenharmony_ci 1962306a36Sopenharmony_ci#define RANGE_NUM 16 2062306a36Sopenharmony_ci#define AMD_NB_F1_CONFIG_MAP_RANGES 4 2162306a36Sopenharmony_ci 2262306a36Sopenharmony_cistruct amd_hostbridge { 2362306a36Sopenharmony_ci u32 bus; 2462306a36Sopenharmony_ci u32 slot; 2562306a36Sopenharmony_ci u32 device; 2662306a36Sopenharmony_ci}; 2762306a36Sopenharmony_ci 2862306a36Sopenharmony_ci/* 2962306a36Sopenharmony_ci * IMPORTANT NOTE: 3062306a36Sopenharmony_ci * hb_probes[] and early_root_info_init() is in maintenance mode. 3162306a36Sopenharmony_ci * It only supports K8, Fam10h, Fam11h, and Fam15h_00h-0fh . 3262306a36Sopenharmony_ci * Future processor will rely on information in ACPI. 3362306a36Sopenharmony_ci */ 3462306a36Sopenharmony_cistatic struct amd_hostbridge hb_probes[] __initdata = { 3562306a36Sopenharmony_ci { 0, 0x18, 0x1100 }, /* K8 */ 3662306a36Sopenharmony_ci { 0, 0x18, 0x1200 }, /* Family10h */ 3762306a36Sopenharmony_ci { 0xff, 0, 0x1200 }, /* Family10h */ 3862306a36Sopenharmony_ci { 0, 0x18, 0x1300 }, /* Family11h */ 3962306a36Sopenharmony_ci { 0, 0x18, 0x1600 }, /* Family15h */ 4062306a36Sopenharmony_ci}; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_cistatic struct pci_root_info __init *find_pci_root_info(int node, int link) 4362306a36Sopenharmony_ci{ 4462306a36Sopenharmony_ci struct pci_root_info *info; 4562306a36Sopenharmony_ci 4662306a36Sopenharmony_ci /* find the position */ 4762306a36Sopenharmony_ci list_for_each_entry(info, &pci_root_infos, list) 4862306a36Sopenharmony_ci if (info->node == node && info->link == link) 4962306a36Sopenharmony_ci return info; 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_ci return NULL; 5262306a36Sopenharmony_ci} 5362306a36Sopenharmony_ci 5462306a36Sopenharmony_cistatic inline resource_size_t cap_resource(u64 val) 5562306a36Sopenharmony_ci{ 5662306a36Sopenharmony_ci if (val > RESOURCE_SIZE_MAX) 5762306a36Sopenharmony_ci return RESOURCE_SIZE_MAX; 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci return val; 6062306a36Sopenharmony_ci} 6162306a36Sopenharmony_ci 6262306a36Sopenharmony_ci/** 6362306a36Sopenharmony_ci * early_root_info_init() 6462306a36Sopenharmony_ci * called before pcibios_scan_root and pci_scan_bus 6562306a36Sopenharmony_ci * fills the mp_bus_to_cpumask array based according 6662306a36Sopenharmony_ci * to the LDT Bus Number Registers found in the northbridge. 6762306a36Sopenharmony_ci */ 6862306a36Sopenharmony_cistatic int __init early_root_info_init(void) 6962306a36Sopenharmony_ci{ 7062306a36Sopenharmony_ci int i; 7162306a36Sopenharmony_ci unsigned bus; 7262306a36Sopenharmony_ci unsigned slot; 7362306a36Sopenharmony_ci int node; 7462306a36Sopenharmony_ci int link; 7562306a36Sopenharmony_ci int def_node; 7662306a36Sopenharmony_ci int def_link; 7762306a36Sopenharmony_ci struct pci_root_info *info; 7862306a36Sopenharmony_ci u32 reg; 7962306a36Sopenharmony_ci u64 start; 8062306a36Sopenharmony_ci u64 end; 8162306a36Sopenharmony_ci struct range range[RANGE_NUM]; 8262306a36Sopenharmony_ci u64 val; 8362306a36Sopenharmony_ci u32 address; 8462306a36Sopenharmony_ci bool found; 8562306a36Sopenharmony_ci struct resource fam10h_mmconf_res, *fam10h_mmconf; 8662306a36Sopenharmony_ci u64 fam10h_mmconf_start; 8762306a36Sopenharmony_ci u64 fam10h_mmconf_end; 8862306a36Sopenharmony_ci 8962306a36Sopenharmony_ci if (!early_pci_allowed()) 9062306a36Sopenharmony_ci return -1; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci found = false; 9362306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(hb_probes); i++) { 9462306a36Sopenharmony_ci u32 id; 9562306a36Sopenharmony_ci u16 device; 9662306a36Sopenharmony_ci u16 vendor; 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci bus = hb_probes[i].bus; 9962306a36Sopenharmony_ci slot = hb_probes[i].slot; 10062306a36Sopenharmony_ci id = read_pci_config(bus, slot, 0, PCI_VENDOR_ID); 10162306a36Sopenharmony_ci vendor = id & 0xffff; 10262306a36Sopenharmony_ci device = (id>>16) & 0xffff; 10362306a36Sopenharmony_ci 10462306a36Sopenharmony_ci if (vendor != PCI_VENDOR_ID_AMD && 10562306a36Sopenharmony_ci vendor != PCI_VENDOR_ID_HYGON) 10662306a36Sopenharmony_ci continue; 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci if (hb_probes[i].device == device) { 10962306a36Sopenharmony_ci found = true; 11062306a36Sopenharmony_ci break; 11162306a36Sopenharmony_ci } 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci if (!found) 11562306a36Sopenharmony_ci return 0; 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci /* 11862306a36Sopenharmony_ci * We should learn topology and routing information from _PXM and 11962306a36Sopenharmony_ci * _CRS methods in the ACPI namespace. We extract node numbers 12062306a36Sopenharmony_ci * here to work around BIOSes that don't supply _PXM. 12162306a36Sopenharmony_ci */ 12262306a36Sopenharmony_ci for (i = 0; i < AMD_NB_F1_CONFIG_MAP_RANGES; i++) { 12362306a36Sopenharmony_ci int min_bus; 12462306a36Sopenharmony_ci int max_bus; 12562306a36Sopenharmony_ci reg = read_pci_config(bus, slot, 1, 12662306a36Sopenharmony_ci AMD_NB_F1_CONFIG_MAP_REG + (i << 2)); 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci /* Check if that register is enabled for bus range */ 12962306a36Sopenharmony_ci if ((reg & 7) != 3) 13062306a36Sopenharmony_ci continue; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci min_bus = (reg >> 16) & 0xff; 13362306a36Sopenharmony_ci max_bus = (reg >> 24) & 0xff; 13462306a36Sopenharmony_ci node = (reg >> 4) & 0x07; 13562306a36Sopenharmony_ci link = (reg >> 8) & 0x03; 13662306a36Sopenharmony_ci 13762306a36Sopenharmony_ci alloc_pci_root_info(min_bus, max_bus, node, link); 13862306a36Sopenharmony_ci } 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci /* 14162306a36Sopenharmony_ci * The following code extracts routing information for use on old 14262306a36Sopenharmony_ci * systems where Linux doesn't automatically use host bridge _CRS 14362306a36Sopenharmony_ci * methods (or when the user specifies "pci=nocrs"). 14462306a36Sopenharmony_ci * 14562306a36Sopenharmony_ci * We only do this through Fam11h, because _CRS should be enough on 14662306a36Sopenharmony_ci * newer systems. 14762306a36Sopenharmony_ci */ 14862306a36Sopenharmony_ci if (boot_cpu_data.x86 > 0x11) 14962306a36Sopenharmony_ci return 0; 15062306a36Sopenharmony_ci 15162306a36Sopenharmony_ci /* get the default node and link for left over res */ 15262306a36Sopenharmony_ci reg = read_pci_config(bus, slot, 0, AMD_NB_F0_NODE_ID); 15362306a36Sopenharmony_ci def_node = (reg >> 8) & 0x07; 15462306a36Sopenharmony_ci reg = read_pci_config(bus, slot, 0, AMD_NB_F0_UNIT_ID); 15562306a36Sopenharmony_ci def_link = (reg >> 8) & 0x03; 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci memset(range, 0, sizeof(range)); 15862306a36Sopenharmony_ci add_range(range, RANGE_NUM, 0, 0, 0xffff + 1); 15962306a36Sopenharmony_ci /* io port resource */ 16062306a36Sopenharmony_ci for (i = 0; i < 4; i++) { 16162306a36Sopenharmony_ci reg = read_pci_config(bus, slot, 1, 0xc0 + (i << 3)); 16262306a36Sopenharmony_ci if (!(reg & 3)) 16362306a36Sopenharmony_ci continue; 16462306a36Sopenharmony_ci 16562306a36Sopenharmony_ci start = reg & 0xfff000; 16662306a36Sopenharmony_ci reg = read_pci_config(bus, slot, 1, 0xc4 + (i << 3)); 16762306a36Sopenharmony_ci node = reg & 0x07; 16862306a36Sopenharmony_ci link = (reg >> 4) & 0x03; 16962306a36Sopenharmony_ci end = (reg & 0xfff000) | 0xfff; 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci info = find_pci_root_info(node, link); 17262306a36Sopenharmony_ci if (!info) 17362306a36Sopenharmony_ci continue; /* not found */ 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci printk(KERN_DEBUG "node %d link %d: io port [%llx, %llx]\n", 17662306a36Sopenharmony_ci node, link, start, end); 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci /* kernel only handle 16 bit only */ 17962306a36Sopenharmony_ci if (end > 0xffff) 18062306a36Sopenharmony_ci end = 0xffff; 18162306a36Sopenharmony_ci update_res(info, start, end, IORESOURCE_IO, 1); 18262306a36Sopenharmony_ci subtract_range(range, RANGE_NUM, start, end + 1); 18362306a36Sopenharmony_ci } 18462306a36Sopenharmony_ci /* add left over io port range to def node/link, [0, 0xffff] */ 18562306a36Sopenharmony_ci /* find the position */ 18662306a36Sopenharmony_ci info = find_pci_root_info(def_node, def_link); 18762306a36Sopenharmony_ci if (info) { 18862306a36Sopenharmony_ci for (i = 0; i < RANGE_NUM; i++) { 18962306a36Sopenharmony_ci if (!range[i].end) 19062306a36Sopenharmony_ci continue; 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci update_res(info, range[i].start, range[i].end - 1, 19362306a36Sopenharmony_ci IORESOURCE_IO, 1); 19462306a36Sopenharmony_ci } 19562306a36Sopenharmony_ci } 19662306a36Sopenharmony_ci 19762306a36Sopenharmony_ci memset(range, 0, sizeof(range)); 19862306a36Sopenharmony_ci /* 0xfd00000000-0xffffffffff for HT */ 19962306a36Sopenharmony_ci end = cap_resource((0xfdULL<<32) - 1); 20062306a36Sopenharmony_ci end++; 20162306a36Sopenharmony_ci add_range(range, RANGE_NUM, 0, 0, end); 20262306a36Sopenharmony_ci 20362306a36Sopenharmony_ci /* need to take out [0, TOM) for RAM*/ 20462306a36Sopenharmony_ci address = MSR_K8_TOP_MEM1; 20562306a36Sopenharmony_ci rdmsrl(address, val); 20662306a36Sopenharmony_ci end = (val & 0xffffff800000ULL); 20762306a36Sopenharmony_ci printk(KERN_INFO "TOM: %016llx aka %lldM\n", end, end>>20); 20862306a36Sopenharmony_ci if (end < (1ULL<<32)) 20962306a36Sopenharmony_ci subtract_range(range, RANGE_NUM, 0, end); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci /* get mmconfig */ 21262306a36Sopenharmony_ci fam10h_mmconf = amd_get_mmconfig_range(&fam10h_mmconf_res); 21362306a36Sopenharmony_ci /* need to take out mmconf range */ 21462306a36Sopenharmony_ci if (fam10h_mmconf) { 21562306a36Sopenharmony_ci printk(KERN_DEBUG "Fam 10h mmconf %pR\n", fam10h_mmconf); 21662306a36Sopenharmony_ci fam10h_mmconf_start = fam10h_mmconf->start; 21762306a36Sopenharmony_ci fam10h_mmconf_end = fam10h_mmconf->end; 21862306a36Sopenharmony_ci subtract_range(range, RANGE_NUM, fam10h_mmconf_start, 21962306a36Sopenharmony_ci fam10h_mmconf_end + 1); 22062306a36Sopenharmony_ci } else { 22162306a36Sopenharmony_ci fam10h_mmconf_start = 0; 22262306a36Sopenharmony_ci fam10h_mmconf_end = 0; 22362306a36Sopenharmony_ci } 22462306a36Sopenharmony_ci 22562306a36Sopenharmony_ci /* mmio resource */ 22662306a36Sopenharmony_ci for (i = 0; i < 8; i++) { 22762306a36Sopenharmony_ci reg = read_pci_config(bus, slot, 1, 0x80 + (i << 3)); 22862306a36Sopenharmony_ci if (!(reg & 3)) 22962306a36Sopenharmony_ci continue; 23062306a36Sopenharmony_ci 23162306a36Sopenharmony_ci start = reg & 0xffffff00; /* 39:16 on 31:8*/ 23262306a36Sopenharmony_ci start <<= 8; 23362306a36Sopenharmony_ci reg = read_pci_config(bus, slot, 1, 0x84 + (i << 3)); 23462306a36Sopenharmony_ci node = reg & 0x07; 23562306a36Sopenharmony_ci link = (reg >> 4) & 0x03; 23662306a36Sopenharmony_ci end = (reg & 0xffffff00); 23762306a36Sopenharmony_ci end <<= 8; 23862306a36Sopenharmony_ci end |= 0xffff; 23962306a36Sopenharmony_ci 24062306a36Sopenharmony_ci info = find_pci_root_info(node, link); 24162306a36Sopenharmony_ci 24262306a36Sopenharmony_ci if (!info) 24362306a36Sopenharmony_ci continue; 24462306a36Sopenharmony_ci 24562306a36Sopenharmony_ci printk(KERN_DEBUG "node %d link %d: mmio [%llx, %llx]", 24662306a36Sopenharmony_ci node, link, start, end); 24762306a36Sopenharmony_ci /* 24862306a36Sopenharmony_ci * some sick allocation would have range overlap with fam10h 24962306a36Sopenharmony_ci * mmconf range, so need to update start and end. 25062306a36Sopenharmony_ci */ 25162306a36Sopenharmony_ci if (fam10h_mmconf_end) { 25262306a36Sopenharmony_ci int changed = 0; 25362306a36Sopenharmony_ci u64 endx = 0; 25462306a36Sopenharmony_ci if (start >= fam10h_mmconf_start && 25562306a36Sopenharmony_ci start <= fam10h_mmconf_end) { 25662306a36Sopenharmony_ci start = fam10h_mmconf_end + 1; 25762306a36Sopenharmony_ci changed = 1; 25862306a36Sopenharmony_ci } 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci if (end >= fam10h_mmconf_start && 26162306a36Sopenharmony_ci end <= fam10h_mmconf_end) { 26262306a36Sopenharmony_ci end = fam10h_mmconf_start - 1; 26362306a36Sopenharmony_ci changed = 1; 26462306a36Sopenharmony_ci } 26562306a36Sopenharmony_ci 26662306a36Sopenharmony_ci if (start < fam10h_mmconf_start && 26762306a36Sopenharmony_ci end > fam10h_mmconf_end) { 26862306a36Sopenharmony_ci /* we got a hole */ 26962306a36Sopenharmony_ci endx = fam10h_mmconf_start - 1; 27062306a36Sopenharmony_ci update_res(info, start, endx, IORESOURCE_MEM, 0); 27162306a36Sopenharmony_ci subtract_range(range, RANGE_NUM, start, 27262306a36Sopenharmony_ci endx + 1); 27362306a36Sopenharmony_ci printk(KERN_CONT " ==> [%llx, %llx]", start, endx); 27462306a36Sopenharmony_ci start = fam10h_mmconf_end + 1; 27562306a36Sopenharmony_ci changed = 1; 27662306a36Sopenharmony_ci } 27762306a36Sopenharmony_ci if (changed) { 27862306a36Sopenharmony_ci if (start <= end) { 27962306a36Sopenharmony_ci printk(KERN_CONT " %s [%llx, %llx]", endx ? "and" : "==>", start, end); 28062306a36Sopenharmony_ci } else { 28162306a36Sopenharmony_ci printk(KERN_CONT "%s\n", endx?"":" ==> none"); 28262306a36Sopenharmony_ci continue; 28362306a36Sopenharmony_ci } 28462306a36Sopenharmony_ci } 28562306a36Sopenharmony_ci } 28662306a36Sopenharmony_ci 28762306a36Sopenharmony_ci update_res(info, cap_resource(start), cap_resource(end), 28862306a36Sopenharmony_ci IORESOURCE_MEM, 1); 28962306a36Sopenharmony_ci subtract_range(range, RANGE_NUM, start, end + 1); 29062306a36Sopenharmony_ci printk(KERN_CONT "\n"); 29162306a36Sopenharmony_ci } 29262306a36Sopenharmony_ci 29362306a36Sopenharmony_ci /* need to take out [4G, TOM2) for RAM*/ 29462306a36Sopenharmony_ci /* SYS_CFG */ 29562306a36Sopenharmony_ci address = MSR_AMD64_SYSCFG; 29662306a36Sopenharmony_ci rdmsrl(address, val); 29762306a36Sopenharmony_ci /* TOP_MEM2 is enabled? */ 29862306a36Sopenharmony_ci if (val & (1<<21)) { 29962306a36Sopenharmony_ci /* TOP_MEM2 */ 30062306a36Sopenharmony_ci address = MSR_K8_TOP_MEM2; 30162306a36Sopenharmony_ci rdmsrl(address, val); 30262306a36Sopenharmony_ci end = (val & 0xffffff800000ULL); 30362306a36Sopenharmony_ci printk(KERN_INFO "TOM2: %016llx aka %lldM\n", end, end>>20); 30462306a36Sopenharmony_ci subtract_range(range, RANGE_NUM, 1ULL<<32, end); 30562306a36Sopenharmony_ci } 30662306a36Sopenharmony_ci 30762306a36Sopenharmony_ci /* 30862306a36Sopenharmony_ci * add left over mmio range to def node/link ? 30962306a36Sopenharmony_ci * that is tricky, just record range in from start_min to 4G 31062306a36Sopenharmony_ci */ 31162306a36Sopenharmony_ci info = find_pci_root_info(def_node, def_link); 31262306a36Sopenharmony_ci if (info) { 31362306a36Sopenharmony_ci for (i = 0; i < RANGE_NUM; i++) { 31462306a36Sopenharmony_ci if (!range[i].end) 31562306a36Sopenharmony_ci continue; 31662306a36Sopenharmony_ci 31762306a36Sopenharmony_ci update_res(info, cap_resource(range[i].start), 31862306a36Sopenharmony_ci cap_resource(range[i].end - 1), 31962306a36Sopenharmony_ci IORESOURCE_MEM, 1); 32062306a36Sopenharmony_ci } 32162306a36Sopenharmony_ci } 32262306a36Sopenharmony_ci 32362306a36Sopenharmony_ci list_for_each_entry(info, &pci_root_infos, list) { 32462306a36Sopenharmony_ci int busnum; 32562306a36Sopenharmony_ci struct pci_root_res *root_res; 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci busnum = info->busn.start; 32862306a36Sopenharmony_ci printk(KERN_DEBUG "bus: %pR on node %x link %x\n", 32962306a36Sopenharmony_ci &info->busn, info->node, info->link); 33062306a36Sopenharmony_ci list_for_each_entry(root_res, &info->resources, list) 33162306a36Sopenharmony_ci printk(KERN_DEBUG "bus: %02x %pR\n", 33262306a36Sopenharmony_ci busnum, &root_res->res); 33362306a36Sopenharmony_ci } 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci return 0; 33662306a36Sopenharmony_ci} 33762306a36Sopenharmony_ci 33862306a36Sopenharmony_ci#define ENABLE_CF8_EXT_CFG (1ULL << 46) 33962306a36Sopenharmony_ci 34062306a36Sopenharmony_cistatic int amd_bus_cpu_online(unsigned int cpu) 34162306a36Sopenharmony_ci{ 34262306a36Sopenharmony_ci u64 reg; 34362306a36Sopenharmony_ci 34462306a36Sopenharmony_ci rdmsrl(MSR_AMD64_NB_CFG, reg); 34562306a36Sopenharmony_ci if (!(reg & ENABLE_CF8_EXT_CFG)) { 34662306a36Sopenharmony_ci reg |= ENABLE_CF8_EXT_CFG; 34762306a36Sopenharmony_ci wrmsrl(MSR_AMD64_NB_CFG, reg); 34862306a36Sopenharmony_ci } 34962306a36Sopenharmony_ci return 0; 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic void __init pci_enable_pci_io_ecs(void) 35362306a36Sopenharmony_ci{ 35462306a36Sopenharmony_ci#ifdef CONFIG_AMD_NB 35562306a36Sopenharmony_ci unsigned int i, n; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci for (n = i = 0; !n && amd_nb_bus_dev_ranges[i].dev_limit; ++i) { 35862306a36Sopenharmony_ci u8 bus = amd_nb_bus_dev_ranges[i].bus; 35962306a36Sopenharmony_ci u8 slot = amd_nb_bus_dev_ranges[i].dev_base; 36062306a36Sopenharmony_ci u8 limit = amd_nb_bus_dev_ranges[i].dev_limit; 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci for (; slot < limit; ++slot) { 36362306a36Sopenharmony_ci u32 val = read_pci_config(bus, slot, 3, 0); 36462306a36Sopenharmony_ci 36562306a36Sopenharmony_ci if (!early_is_amd_nb(val)) 36662306a36Sopenharmony_ci continue; 36762306a36Sopenharmony_ci 36862306a36Sopenharmony_ci val = read_pci_config(bus, slot, 3, 0x8c); 36962306a36Sopenharmony_ci if (!(val & (ENABLE_CF8_EXT_CFG >> 32))) { 37062306a36Sopenharmony_ci val |= ENABLE_CF8_EXT_CFG >> 32; 37162306a36Sopenharmony_ci write_pci_config(bus, slot, 3, 0x8c, val); 37262306a36Sopenharmony_ci } 37362306a36Sopenharmony_ci ++n; 37462306a36Sopenharmony_ci } 37562306a36Sopenharmony_ci } 37662306a36Sopenharmony_ci#endif 37762306a36Sopenharmony_ci} 37862306a36Sopenharmony_ci 37962306a36Sopenharmony_cistatic int __init pci_io_ecs_init(void) 38062306a36Sopenharmony_ci{ 38162306a36Sopenharmony_ci int ret; 38262306a36Sopenharmony_ci 38362306a36Sopenharmony_ci /* assume all cpus from fam10h have IO ECS */ 38462306a36Sopenharmony_ci if (boot_cpu_data.x86 < 0x10) 38562306a36Sopenharmony_ci return 0; 38662306a36Sopenharmony_ci 38762306a36Sopenharmony_ci /* Try the PCI method first. */ 38862306a36Sopenharmony_ci if (early_pci_allowed()) 38962306a36Sopenharmony_ci pci_enable_pci_io_ecs(); 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_ci ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "pci/amd_bus:online", 39262306a36Sopenharmony_ci amd_bus_cpu_online, NULL); 39362306a36Sopenharmony_ci WARN_ON(ret < 0); 39462306a36Sopenharmony_ci 39562306a36Sopenharmony_ci pci_probe |= PCI_HAS_IO_ECS; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci return 0; 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_cistatic int __init amd_postcore_init(void) 40162306a36Sopenharmony_ci{ 40262306a36Sopenharmony_ci if (boot_cpu_data.x86_vendor != X86_VENDOR_AMD && 40362306a36Sopenharmony_ci boot_cpu_data.x86_vendor != X86_VENDOR_HYGON) 40462306a36Sopenharmony_ci return 0; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci early_root_info_init(); 40762306a36Sopenharmony_ci pci_io_ecs_init(); 40862306a36Sopenharmony_ci 40962306a36Sopenharmony_ci return 0; 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_cipostcore_initcall(amd_postcore_init); 413