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