18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * AMD NUMA support. 48c2ecf20Sopenharmony_ci * Discover the memory map and associated nodes. 58c2ecf20Sopenharmony_ci * 68c2ecf20Sopenharmony_ci * This version reads it directly from the AMD northbridge. 78c2ecf20Sopenharmony_ci * 88c2ecf20Sopenharmony_ci * Copyright 2002,2003 Andi Kleen, SuSE Labs. 98c2ecf20Sopenharmony_ci */ 108c2ecf20Sopenharmony_ci#include <linux/kernel.h> 118c2ecf20Sopenharmony_ci#include <linux/init.h> 128c2ecf20Sopenharmony_ci#include <linux/string.h> 138c2ecf20Sopenharmony_ci#include <linux/nodemask.h> 148c2ecf20Sopenharmony_ci#include <linux/memblock.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <asm/io.h> 178c2ecf20Sopenharmony_ci#include <linux/pci_ids.h> 188c2ecf20Sopenharmony_ci#include <linux/acpi.h> 198c2ecf20Sopenharmony_ci#include <asm/types.h> 208c2ecf20Sopenharmony_ci#include <asm/mmzone.h> 218c2ecf20Sopenharmony_ci#include <asm/proto.h> 228c2ecf20Sopenharmony_ci#include <asm/e820/api.h> 238c2ecf20Sopenharmony_ci#include <asm/pci-direct.h> 248c2ecf20Sopenharmony_ci#include <asm/numa.h> 258c2ecf20Sopenharmony_ci#include <asm/mpspec.h> 268c2ecf20Sopenharmony_ci#include <asm/apic.h> 278c2ecf20Sopenharmony_ci#include <asm/amd_nb.h> 288c2ecf20Sopenharmony_ci 298c2ecf20Sopenharmony_cistatic unsigned char __initdata nodeids[8]; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_cistatic __init int find_northbridge(void) 328c2ecf20Sopenharmony_ci{ 338c2ecf20Sopenharmony_ci int num; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci for (num = 0; num < 32; num++) { 368c2ecf20Sopenharmony_ci u32 header; 378c2ecf20Sopenharmony_ci 388c2ecf20Sopenharmony_ci header = read_pci_config(0, num, 0, 0x00); 398c2ecf20Sopenharmony_ci if (header != (PCI_VENDOR_ID_AMD | (0x1100<<16)) && 408c2ecf20Sopenharmony_ci header != (PCI_VENDOR_ID_AMD | (0x1200<<16)) && 418c2ecf20Sopenharmony_ci header != (PCI_VENDOR_ID_AMD | (0x1300<<16))) 428c2ecf20Sopenharmony_ci continue; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci header = read_pci_config(0, num, 1, 0x00); 458c2ecf20Sopenharmony_ci if (header != (PCI_VENDOR_ID_AMD | (0x1101<<16)) && 468c2ecf20Sopenharmony_ci header != (PCI_VENDOR_ID_AMD | (0x1201<<16)) && 478c2ecf20Sopenharmony_ci header != (PCI_VENDOR_ID_AMD | (0x1301<<16))) 488c2ecf20Sopenharmony_ci continue; 498c2ecf20Sopenharmony_ci return num; 508c2ecf20Sopenharmony_ci } 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci return -ENOENT; 538c2ecf20Sopenharmony_ci} 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ciint __init amd_numa_init(void) 568c2ecf20Sopenharmony_ci{ 578c2ecf20Sopenharmony_ci u64 start = PFN_PHYS(0); 588c2ecf20Sopenharmony_ci u64 end = PFN_PHYS(max_pfn); 598c2ecf20Sopenharmony_ci unsigned numnodes; 608c2ecf20Sopenharmony_ci u64 prevbase; 618c2ecf20Sopenharmony_ci int i, j, nb; 628c2ecf20Sopenharmony_ci u32 nodeid, reg; 638c2ecf20Sopenharmony_ci unsigned int bits, cores, apicid_base; 648c2ecf20Sopenharmony_ci 658c2ecf20Sopenharmony_ci if (!early_pci_allowed()) 668c2ecf20Sopenharmony_ci return -EINVAL; 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci nb = find_northbridge(); 698c2ecf20Sopenharmony_ci if (nb < 0) 708c2ecf20Sopenharmony_ci return nb; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci pr_info("Scanning NUMA topology in Northbridge %d\n", nb); 738c2ecf20Sopenharmony_ci 748c2ecf20Sopenharmony_ci reg = read_pci_config(0, nb, 0, 0x60); 758c2ecf20Sopenharmony_ci numnodes = ((reg >> 4) & 0xF) + 1; 768c2ecf20Sopenharmony_ci if (numnodes <= 1) 778c2ecf20Sopenharmony_ci return -ENOENT; 788c2ecf20Sopenharmony_ci 798c2ecf20Sopenharmony_ci pr_info("Number of physical nodes %d\n", numnodes); 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_ci prevbase = 0; 828c2ecf20Sopenharmony_ci for (i = 0; i < 8; i++) { 838c2ecf20Sopenharmony_ci u64 base, limit; 848c2ecf20Sopenharmony_ci 858c2ecf20Sopenharmony_ci base = read_pci_config(0, nb, 1, 0x40 + i*8); 868c2ecf20Sopenharmony_ci limit = read_pci_config(0, nb, 1, 0x44 + i*8); 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_ci nodeids[i] = nodeid = limit & 7; 898c2ecf20Sopenharmony_ci if ((base & 3) == 0) { 908c2ecf20Sopenharmony_ci if (i < numnodes) 918c2ecf20Sopenharmony_ci pr_info("Skipping disabled node %d\n", i); 928c2ecf20Sopenharmony_ci continue; 938c2ecf20Sopenharmony_ci } 948c2ecf20Sopenharmony_ci if (nodeid >= numnodes) { 958c2ecf20Sopenharmony_ci pr_info("Ignoring excess node %d (%Lx:%Lx)\n", nodeid, 968c2ecf20Sopenharmony_ci base, limit); 978c2ecf20Sopenharmony_ci continue; 988c2ecf20Sopenharmony_ci } 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci if (!limit) { 1018c2ecf20Sopenharmony_ci pr_info("Skipping node entry %d (base %Lx)\n", 1028c2ecf20Sopenharmony_ci i, base); 1038c2ecf20Sopenharmony_ci continue; 1048c2ecf20Sopenharmony_ci } 1058c2ecf20Sopenharmony_ci if ((base >> 8) & 3 || (limit >> 8) & 3) { 1068c2ecf20Sopenharmony_ci pr_err("Node %d using interleaving mode %Lx/%Lx\n", 1078c2ecf20Sopenharmony_ci nodeid, (base >> 8) & 3, (limit >> 8) & 3); 1088c2ecf20Sopenharmony_ci return -EINVAL; 1098c2ecf20Sopenharmony_ci } 1108c2ecf20Sopenharmony_ci if (node_isset(nodeid, numa_nodes_parsed)) { 1118c2ecf20Sopenharmony_ci pr_info("Node %d already present, skipping\n", 1128c2ecf20Sopenharmony_ci nodeid); 1138c2ecf20Sopenharmony_ci continue; 1148c2ecf20Sopenharmony_ci } 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci limit >>= 16; 1178c2ecf20Sopenharmony_ci limit++; 1188c2ecf20Sopenharmony_ci limit <<= 24; 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci if (limit > end) 1218c2ecf20Sopenharmony_ci limit = end; 1228c2ecf20Sopenharmony_ci if (limit <= base) 1238c2ecf20Sopenharmony_ci continue; 1248c2ecf20Sopenharmony_ci 1258c2ecf20Sopenharmony_ci base >>= 16; 1268c2ecf20Sopenharmony_ci base <<= 24; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci if (base < start) 1298c2ecf20Sopenharmony_ci base = start; 1308c2ecf20Sopenharmony_ci if (limit > end) 1318c2ecf20Sopenharmony_ci limit = end; 1328c2ecf20Sopenharmony_ci if (limit == base) { 1338c2ecf20Sopenharmony_ci pr_err("Empty node %d\n", nodeid); 1348c2ecf20Sopenharmony_ci continue; 1358c2ecf20Sopenharmony_ci } 1368c2ecf20Sopenharmony_ci if (limit < base) { 1378c2ecf20Sopenharmony_ci pr_err("Node %d bogus settings %Lx-%Lx.\n", 1388c2ecf20Sopenharmony_ci nodeid, base, limit); 1398c2ecf20Sopenharmony_ci continue; 1408c2ecf20Sopenharmony_ci } 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci /* Could sort here, but pun for now. Should not happen anyroads. */ 1438c2ecf20Sopenharmony_ci if (prevbase > base) { 1448c2ecf20Sopenharmony_ci pr_err("Node map not sorted %Lx,%Lx\n", 1458c2ecf20Sopenharmony_ci prevbase, base); 1468c2ecf20Sopenharmony_ci return -EINVAL; 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci pr_info("Node %d MemBase %016Lx Limit %016Lx\n", 1508c2ecf20Sopenharmony_ci nodeid, base, limit); 1518c2ecf20Sopenharmony_ci 1528c2ecf20Sopenharmony_ci prevbase = base; 1538c2ecf20Sopenharmony_ci numa_add_memblk(nodeid, base, limit); 1548c2ecf20Sopenharmony_ci node_set(nodeid, numa_nodes_parsed); 1558c2ecf20Sopenharmony_ci } 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci if (!nodes_weight(numa_nodes_parsed)) 1588c2ecf20Sopenharmony_ci return -ENOENT; 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci /* 1618c2ecf20Sopenharmony_ci * We seem to have valid NUMA configuration. Map apicids to nodes 1628c2ecf20Sopenharmony_ci * using the coreid bits from early_identify_cpu. 1638c2ecf20Sopenharmony_ci */ 1648c2ecf20Sopenharmony_ci bits = boot_cpu_data.x86_coreid_bits; 1658c2ecf20Sopenharmony_ci cores = 1 << bits; 1668c2ecf20Sopenharmony_ci apicid_base = 0; 1678c2ecf20Sopenharmony_ci 1688c2ecf20Sopenharmony_ci /* 1698c2ecf20Sopenharmony_ci * get boot-time SMP configuration: 1708c2ecf20Sopenharmony_ci */ 1718c2ecf20Sopenharmony_ci early_get_smp_config(); 1728c2ecf20Sopenharmony_ci 1738c2ecf20Sopenharmony_ci if (boot_cpu_physical_apicid > 0) { 1748c2ecf20Sopenharmony_ci pr_info("BSP APIC ID: %02x\n", boot_cpu_physical_apicid); 1758c2ecf20Sopenharmony_ci apicid_base = boot_cpu_physical_apicid; 1768c2ecf20Sopenharmony_ci } 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci for_each_node_mask(i, numa_nodes_parsed) 1798c2ecf20Sopenharmony_ci for (j = apicid_base; j < cores + apicid_base; j++) 1808c2ecf20Sopenharmony_ci set_apicid_to_node((i << bits) + j, i); 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_ci return 0; 1838c2ecf20Sopenharmony_ci} 184