162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * AMD NUMA support. 462306a36Sopenharmony_ci * Discover the memory map and associated nodes. 562306a36Sopenharmony_ci * 662306a36Sopenharmony_ci * This version reads it directly from the AMD northbridge. 762306a36Sopenharmony_ci * 862306a36Sopenharmony_ci * Copyright 2002,2003 Andi Kleen, SuSE Labs. 962306a36Sopenharmony_ci */ 1062306a36Sopenharmony_ci#include <linux/kernel.h> 1162306a36Sopenharmony_ci#include <linux/init.h> 1262306a36Sopenharmony_ci#include <linux/string.h> 1362306a36Sopenharmony_ci#include <linux/nodemask.h> 1462306a36Sopenharmony_ci#include <linux/memblock.h> 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci#include <asm/io.h> 1762306a36Sopenharmony_ci#include <linux/pci_ids.h> 1862306a36Sopenharmony_ci#include <linux/acpi.h> 1962306a36Sopenharmony_ci#include <asm/types.h> 2062306a36Sopenharmony_ci#include <asm/mmzone.h> 2162306a36Sopenharmony_ci#include <asm/proto.h> 2262306a36Sopenharmony_ci#include <asm/e820/api.h> 2362306a36Sopenharmony_ci#include <asm/pci-direct.h> 2462306a36Sopenharmony_ci#include <asm/numa.h> 2562306a36Sopenharmony_ci#include <asm/mpspec.h> 2662306a36Sopenharmony_ci#include <asm/apic.h> 2762306a36Sopenharmony_ci#include <asm/amd_nb.h> 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic unsigned char __initdata nodeids[8]; 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_cistatic __init int find_northbridge(void) 3262306a36Sopenharmony_ci{ 3362306a36Sopenharmony_ci int num; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci for (num = 0; num < 32; num++) { 3662306a36Sopenharmony_ci u32 header; 3762306a36Sopenharmony_ci 3862306a36Sopenharmony_ci header = read_pci_config(0, num, 0, 0x00); 3962306a36Sopenharmony_ci if (header != (PCI_VENDOR_ID_AMD | (0x1100<<16)) && 4062306a36Sopenharmony_ci header != (PCI_VENDOR_ID_AMD | (0x1200<<16)) && 4162306a36Sopenharmony_ci header != (PCI_VENDOR_ID_AMD | (0x1300<<16))) 4262306a36Sopenharmony_ci continue; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci header = read_pci_config(0, num, 1, 0x00); 4562306a36Sopenharmony_ci if (header != (PCI_VENDOR_ID_AMD | (0x1101<<16)) && 4662306a36Sopenharmony_ci header != (PCI_VENDOR_ID_AMD | (0x1201<<16)) && 4762306a36Sopenharmony_ci header != (PCI_VENDOR_ID_AMD | (0x1301<<16))) 4862306a36Sopenharmony_ci continue; 4962306a36Sopenharmony_ci return num; 5062306a36Sopenharmony_ci } 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci return -ENOENT; 5362306a36Sopenharmony_ci} 5462306a36Sopenharmony_ci 5562306a36Sopenharmony_ciint __init amd_numa_init(void) 5662306a36Sopenharmony_ci{ 5762306a36Sopenharmony_ci u64 start = PFN_PHYS(0); 5862306a36Sopenharmony_ci u64 end = PFN_PHYS(max_pfn); 5962306a36Sopenharmony_ci unsigned numnodes; 6062306a36Sopenharmony_ci u64 prevbase; 6162306a36Sopenharmony_ci int i, j, nb; 6262306a36Sopenharmony_ci u32 nodeid, reg; 6362306a36Sopenharmony_ci unsigned int bits, cores, apicid_base; 6462306a36Sopenharmony_ci 6562306a36Sopenharmony_ci if (!early_pci_allowed()) 6662306a36Sopenharmony_ci return -EINVAL; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci nb = find_northbridge(); 6962306a36Sopenharmony_ci if (nb < 0) 7062306a36Sopenharmony_ci return nb; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_ci pr_info("Scanning NUMA topology in Northbridge %d\n", nb); 7362306a36Sopenharmony_ci 7462306a36Sopenharmony_ci reg = read_pci_config(0, nb, 0, 0x60); 7562306a36Sopenharmony_ci numnodes = ((reg >> 4) & 0xF) + 1; 7662306a36Sopenharmony_ci if (numnodes <= 1) 7762306a36Sopenharmony_ci return -ENOENT; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci pr_info("Number of physical nodes %d\n", numnodes); 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci prevbase = 0; 8262306a36Sopenharmony_ci for (i = 0; i < 8; i++) { 8362306a36Sopenharmony_ci u64 base, limit; 8462306a36Sopenharmony_ci 8562306a36Sopenharmony_ci base = read_pci_config(0, nb, 1, 0x40 + i*8); 8662306a36Sopenharmony_ci limit = read_pci_config(0, nb, 1, 0x44 + i*8); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci nodeids[i] = nodeid = limit & 7; 8962306a36Sopenharmony_ci if ((base & 3) == 0) { 9062306a36Sopenharmony_ci if (i < numnodes) 9162306a36Sopenharmony_ci pr_info("Skipping disabled node %d\n", i); 9262306a36Sopenharmony_ci continue; 9362306a36Sopenharmony_ci } 9462306a36Sopenharmony_ci if (nodeid >= numnodes) { 9562306a36Sopenharmony_ci pr_info("Ignoring excess node %d (%Lx:%Lx)\n", nodeid, 9662306a36Sopenharmony_ci base, limit); 9762306a36Sopenharmony_ci continue; 9862306a36Sopenharmony_ci } 9962306a36Sopenharmony_ci 10062306a36Sopenharmony_ci if (!limit) { 10162306a36Sopenharmony_ci pr_info("Skipping node entry %d (base %Lx)\n", 10262306a36Sopenharmony_ci i, base); 10362306a36Sopenharmony_ci continue; 10462306a36Sopenharmony_ci } 10562306a36Sopenharmony_ci if ((base >> 8) & 3 || (limit >> 8) & 3) { 10662306a36Sopenharmony_ci pr_err("Node %d using interleaving mode %Lx/%Lx\n", 10762306a36Sopenharmony_ci nodeid, (base >> 8) & 3, (limit >> 8) & 3); 10862306a36Sopenharmony_ci return -EINVAL; 10962306a36Sopenharmony_ci } 11062306a36Sopenharmony_ci if (node_isset(nodeid, numa_nodes_parsed)) { 11162306a36Sopenharmony_ci pr_info("Node %d already present, skipping\n", 11262306a36Sopenharmony_ci nodeid); 11362306a36Sopenharmony_ci continue; 11462306a36Sopenharmony_ci } 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci limit >>= 16; 11762306a36Sopenharmony_ci limit++; 11862306a36Sopenharmony_ci limit <<= 24; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci if (limit > end) 12162306a36Sopenharmony_ci limit = end; 12262306a36Sopenharmony_ci if (limit <= base) 12362306a36Sopenharmony_ci continue; 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci base >>= 16; 12662306a36Sopenharmony_ci base <<= 24; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci if (base < start) 12962306a36Sopenharmony_ci base = start; 13062306a36Sopenharmony_ci if (limit > end) 13162306a36Sopenharmony_ci limit = end; 13262306a36Sopenharmony_ci if (limit == base) { 13362306a36Sopenharmony_ci pr_err("Empty node %d\n", nodeid); 13462306a36Sopenharmony_ci continue; 13562306a36Sopenharmony_ci } 13662306a36Sopenharmony_ci if (limit < base) { 13762306a36Sopenharmony_ci pr_err("Node %d bogus settings %Lx-%Lx.\n", 13862306a36Sopenharmony_ci nodeid, base, limit); 13962306a36Sopenharmony_ci continue; 14062306a36Sopenharmony_ci } 14162306a36Sopenharmony_ci 14262306a36Sopenharmony_ci /* Could sort here, but pun for now. Should not happen anyroads. */ 14362306a36Sopenharmony_ci if (prevbase > base) { 14462306a36Sopenharmony_ci pr_err("Node map not sorted %Lx,%Lx\n", 14562306a36Sopenharmony_ci prevbase, base); 14662306a36Sopenharmony_ci return -EINVAL; 14762306a36Sopenharmony_ci } 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci pr_info("Node %d MemBase %016Lx Limit %016Lx\n", 15062306a36Sopenharmony_ci nodeid, base, limit); 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci prevbase = base; 15362306a36Sopenharmony_ci numa_add_memblk(nodeid, base, limit); 15462306a36Sopenharmony_ci node_set(nodeid, numa_nodes_parsed); 15562306a36Sopenharmony_ci } 15662306a36Sopenharmony_ci 15762306a36Sopenharmony_ci if (nodes_empty(numa_nodes_parsed)) 15862306a36Sopenharmony_ci return -ENOENT; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci /* 16162306a36Sopenharmony_ci * We seem to have valid NUMA configuration. Map apicids to nodes 16262306a36Sopenharmony_ci * using the coreid bits from early_identify_cpu. 16362306a36Sopenharmony_ci */ 16462306a36Sopenharmony_ci bits = boot_cpu_data.x86_coreid_bits; 16562306a36Sopenharmony_ci cores = 1 << bits; 16662306a36Sopenharmony_ci apicid_base = 0; 16762306a36Sopenharmony_ci 16862306a36Sopenharmony_ci /* 16962306a36Sopenharmony_ci * get boot-time SMP configuration: 17062306a36Sopenharmony_ci */ 17162306a36Sopenharmony_ci early_get_smp_config(); 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_ci if (boot_cpu_physical_apicid > 0) { 17462306a36Sopenharmony_ci pr_info("BSP APIC ID: %02x\n", boot_cpu_physical_apicid); 17562306a36Sopenharmony_ci apicid_base = boot_cpu_physical_apicid; 17662306a36Sopenharmony_ci } 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci for_each_node_mask(i, numa_nodes_parsed) 17962306a36Sopenharmony_ci for (j = apicid_base; j < cores + apicid_base; j++) 18062306a36Sopenharmony_ci set_apicid_to_node((i << bits) + j, i); 18162306a36Sopenharmony_ci 18262306a36Sopenharmony_ci return 0; 18362306a36Sopenharmony_ci} 184