18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (C) 2010 Loongson Inc. & Lemote Inc. &
48c2ecf20Sopenharmony_ci *                    Institute of Computing Technology
58c2ecf20Sopenharmony_ci * Author:  Xiang Gao, gaoxiang@ict.ac.cn
68c2ecf20Sopenharmony_ci *          Huacai Chen, chenhc@lemote.com
78c2ecf20Sopenharmony_ci *          Xiaofu Meng, Shuangshuang Zhang
88c2ecf20Sopenharmony_ci */
98c2ecf20Sopenharmony_ci#include <linux/init.h>
108c2ecf20Sopenharmony_ci#include <linux/kernel.h>
118c2ecf20Sopenharmony_ci#include <linux/mm.h>
128c2ecf20Sopenharmony_ci#include <linux/mmzone.h>
138c2ecf20Sopenharmony_ci#include <linux/export.h>
148c2ecf20Sopenharmony_ci#include <linux/nodemask.h>
158c2ecf20Sopenharmony_ci#include <linux/swap.h>
168c2ecf20Sopenharmony_ci#include <linux/memblock.h>
178c2ecf20Sopenharmony_ci#include <linux/pfn.h>
188c2ecf20Sopenharmony_ci#include <linux/highmem.h>
198c2ecf20Sopenharmony_ci#include <asm/page.h>
208c2ecf20Sopenharmony_ci#include <asm/pgalloc.h>
218c2ecf20Sopenharmony_ci#include <asm/sections.h>
228c2ecf20Sopenharmony_ci#include <linux/irq.h>
238c2ecf20Sopenharmony_ci#include <asm/bootinfo.h>
248c2ecf20Sopenharmony_ci#include <asm/mc146818-time.h>
258c2ecf20Sopenharmony_ci#include <asm/time.h>
268c2ecf20Sopenharmony_ci#include <asm/wbflush.h>
278c2ecf20Sopenharmony_ci#include <boot_param.h>
288c2ecf20Sopenharmony_ci
298c2ecf20Sopenharmony_cistatic struct pglist_data prealloc__node_data[MAX_NUMNODES];
308c2ecf20Sopenharmony_ciunsigned char __node_distances[MAX_NUMNODES][MAX_NUMNODES];
318c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__node_distances);
328c2ecf20Sopenharmony_cistruct pglist_data *__node_data[MAX_NUMNODES];
338c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__node_data);
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_cicpumask_t __node_cpumask[MAX_NUMNODES];
368c2ecf20Sopenharmony_ciEXPORT_SYMBOL(__node_cpumask);
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic void enable_lpa(void)
398c2ecf20Sopenharmony_ci{
408c2ecf20Sopenharmony_ci	unsigned long value;
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	value = __read_32bit_c0_register($16, 3);
438c2ecf20Sopenharmony_ci	value |= 0x00000080;
448c2ecf20Sopenharmony_ci	__write_32bit_c0_register($16, 3, value);
458c2ecf20Sopenharmony_ci	value = __read_32bit_c0_register($16, 3);
468c2ecf20Sopenharmony_ci	pr_info("CP0_Config3: CP0 16.3 (0x%lx)\n", value);
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci	value = __read_32bit_c0_register($5, 1);
498c2ecf20Sopenharmony_ci	value |= 0x20000000;
508c2ecf20Sopenharmony_ci	__write_32bit_c0_register($5, 1, value);
518c2ecf20Sopenharmony_ci	value = __read_32bit_c0_register($5, 1);
528c2ecf20Sopenharmony_ci	pr_info("CP0_PageGrain: CP0 5.1 (0x%lx)\n", value);
538c2ecf20Sopenharmony_ci}
548c2ecf20Sopenharmony_ci
558c2ecf20Sopenharmony_cistatic void cpu_node_probe(void)
568c2ecf20Sopenharmony_ci{
578c2ecf20Sopenharmony_ci	int i;
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci	nodes_clear(node_possible_map);
608c2ecf20Sopenharmony_ci	nodes_clear(node_online_map);
618c2ecf20Sopenharmony_ci	for (i = 0; i < loongson_sysconf.nr_nodes; i++) {
628c2ecf20Sopenharmony_ci		node_set_state(num_online_nodes(), N_POSSIBLE);
638c2ecf20Sopenharmony_ci		node_set_online(num_online_nodes());
648c2ecf20Sopenharmony_ci	}
658c2ecf20Sopenharmony_ci
668c2ecf20Sopenharmony_ci	pr_info("NUMA: Discovered %d cpus on %d nodes\n",
678c2ecf20Sopenharmony_ci		loongson_sysconf.nr_cpus, num_online_nodes());
688c2ecf20Sopenharmony_ci}
698c2ecf20Sopenharmony_ci
708c2ecf20Sopenharmony_cistatic int __init compute_node_distance(int row, int col)
718c2ecf20Sopenharmony_ci{
728c2ecf20Sopenharmony_ci	int package_row = row * loongson_sysconf.cores_per_node /
738c2ecf20Sopenharmony_ci				loongson_sysconf.cores_per_package;
748c2ecf20Sopenharmony_ci	int package_col = col * loongson_sysconf.cores_per_node /
758c2ecf20Sopenharmony_ci				loongson_sysconf.cores_per_package;
768c2ecf20Sopenharmony_ci
778c2ecf20Sopenharmony_ci	if (col == row)
788c2ecf20Sopenharmony_ci		return LOCAL_DISTANCE;
798c2ecf20Sopenharmony_ci	else if (package_row == package_col)
808c2ecf20Sopenharmony_ci		return 40;
818c2ecf20Sopenharmony_ci	else
828c2ecf20Sopenharmony_ci		return 100;
838c2ecf20Sopenharmony_ci}
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic void __init init_topology_matrix(void)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	int row, col;
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ci	for (row = 0; row < MAX_NUMNODES; row++)
908c2ecf20Sopenharmony_ci		for (col = 0; col < MAX_NUMNODES; col++)
918c2ecf20Sopenharmony_ci			__node_distances[row][col] = -1;
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	for_each_online_node(row) {
948c2ecf20Sopenharmony_ci		for_each_online_node(col) {
958c2ecf20Sopenharmony_ci			__node_distances[row][col] =
968c2ecf20Sopenharmony_ci				compute_node_distance(row, col);
978c2ecf20Sopenharmony_ci		}
988c2ecf20Sopenharmony_ci	}
998c2ecf20Sopenharmony_ci}
1008c2ecf20Sopenharmony_ci
1018c2ecf20Sopenharmony_cistatic void __init szmem(unsigned int node)
1028c2ecf20Sopenharmony_ci{
1038c2ecf20Sopenharmony_ci	u32 i, mem_type;
1048c2ecf20Sopenharmony_ci	static unsigned long num_physpages;
1058c2ecf20Sopenharmony_ci	u64 node_id, node_psize, start_pfn, end_pfn, mem_start, mem_size;
1068c2ecf20Sopenharmony_ci
1078c2ecf20Sopenharmony_ci	/* Parse memory information and activate */
1088c2ecf20Sopenharmony_ci	for (i = 0; i < loongson_memmap->nr_map; i++) {
1098c2ecf20Sopenharmony_ci		node_id = loongson_memmap->map[i].node_id;
1108c2ecf20Sopenharmony_ci		if (node_id != node)
1118c2ecf20Sopenharmony_ci			continue;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci		mem_type = loongson_memmap->map[i].mem_type;
1148c2ecf20Sopenharmony_ci		mem_size = loongson_memmap->map[i].mem_size;
1158c2ecf20Sopenharmony_ci		mem_start = loongson_memmap->map[i].mem_start;
1168c2ecf20Sopenharmony_ci
1178c2ecf20Sopenharmony_ci		switch (mem_type) {
1188c2ecf20Sopenharmony_ci		case SYSTEM_RAM_LOW:
1198c2ecf20Sopenharmony_ci			start_pfn = ((node_id << 44) + mem_start) >> PAGE_SHIFT;
1208c2ecf20Sopenharmony_ci			node_psize = (mem_size << 20) >> PAGE_SHIFT;
1218c2ecf20Sopenharmony_ci			end_pfn  = start_pfn + node_psize;
1228c2ecf20Sopenharmony_ci			num_physpages += node_psize;
1238c2ecf20Sopenharmony_ci			pr_info("Node%d: mem_type:%d, mem_start:0x%llx, mem_size:0x%llx MB\n",
1248c2ecf20Sopenharmony_ci				(u32)node_id, mem_type, mem_start, mem_size);
1258c2ecf20Sopenharmony_ci			pr_info("       start_pfn:0x%llx, end_pfn:0x%llx, num_physpages:0x%lx\n",
1268c2ecf20Sopenharmony_ci				start_pfn, end_pfn, num_physpages);
1278c2ecf20Sopenharmony_ci			memblock_add_node(PFN_PHYS(start_pfn),
1288c2ecf20Sopenharmony_ci				PFN_PHYS(node_psize), node);
1298c2ecf20Sopenharmony_ci			break;
1308c2ecf20Sopenharmony_ci		case SYSTEM_RAM_HIGH:
1318c2ecf20Sopenharmony_ci			start_pfn = ((node_id << 44) + mem_start) >> PAGE_SHIFT;
1328c2ecf20Sopenharmony_ci			node_psize = (mem_size << 20) >> PAGE_SHIFT;
1338c2ecf20Sopenharmony_ci			end_pfn  = start_pfn + node_psize;
1348c2ecf20Sopenharmony_ci			num_physpages += node_psize;
1358c2ecf20Sopenharmony_ci			pr_info("Node%d: mem_type:%d, mem_start:0x%llx, mem_size:0x%llx MB\n",
1368c2ecf20Sopenharmony_ci				(u32)node_id, mem_type, mem_start, mem_size);
1378c2ecf20Sopenharmony_ci			pr_info("       start_pfn:0x%llx, end_pfn:0x%llx, num_physpages:0x%lx\n",
1388c2ecf20Sopenharmony_ci				start_pfn, end_pfn, num_physpages);
1398c2ecf20Sopenharmony_ci			memblock_add_node(PFN_PHYS(start_pfn),
1408c2ecf20Sopenharmony_ci				PFN_PHYS(node_psize), node);
1418c2ecf20Sopenharmony_ci			break;
1428c2ecf20Sopenharmony_ci		case SYSTEM_RAM_RESERVED:
1438c2ecf20Sopenharmony_ci			pr_info("Node%d: mem_type:%d, mem_start:0x%llx, mem_size:0x%llx MB\n",
1448c2ecf20Sopenharmony_ci				(u32)node_id, mem_type, mem_start, mem_size);
1458c2ecf20Sopenharmony_ci			memblock_reserve(((node_id << 44) + mem_start),
1468c2ecf20Sopenharmony_ci				mem_size << 20);
1478c2ecf20Sopenharmony_ci			break;
1488c2ecf20Sopenharmony_ci		}
1498c2ecf20Sopenharmony_ci	}
1508c2ecf20Sopenharmony_ci}
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_cistatic void __init node_mem_init(unsigned int node)
1538c2ecf20Sopenharmony_ci{
1548c2ecf20Sopenharmony_ci	unsigned long node_addrspace_offset;
1558c2ecf20Sopenharmony_ci	unsigned long start_pfn, end_pfn;
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	node_addrspace_offset = nid_to_addrbase(node);
1588c2ecf20Sopenharmony_ci	pr_info("Node%d's addrspace_offset is 0x%lx\n",
1598c2ecf20Sopenharmony_ci			node, node_addrspace_offset);
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci	get_pfn_range_for_nid(node, &start_pfn, &end_pfn);
1628c2ecf20Sopenharmony_ci	pr_info("Node%d: start_pfn=0x%lx, end_pfn=0x%lx\n",
1638c2ecf20Sopenharmony_ci		node, start_pfn, end_pfn);
1648c2ecf20Sopenharmony_ci
1658c2ecf20Sopenharmony_ci	__node_data[node] = prealloc__node_data + node;
1668c2ecf20Sopenharmony_ci
1678c2ecf20Sopenharmony_ci	NODE_DATA(node)->node_start_pfn = start_pfn;
1688c2ecf20Sopenharmony_ci	NODE_DATA(node)->node_spanned_pages = end_pfn - start_pfn;
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	if (node == 0) {
1718c2ecf20Sopenharmony_ci		/* kernel end address */
1728c2ecf20Sopenharmony_ci		unsigned long kernel_end_pfn = PFN_UP(__pa_symbol(&_end));
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci		/* used by finalize_initrd() */
1758c2ecf20Sopenharmony_ci		max_low_pfn = end_pfn;
1768c2ecf20Sopenharmony_ci
1778c2ecf20Sopenharmony_ci		/* Reserve the kernel text/data/bss */
1788c2ecf20Sopenharmony_ci		memblock_reserve(start_pfn << PAGE_SHIFT,
1798c2ecf20Sopenharmony_ci				 ((kernel_end_pfn - start_pfn) << PAGE_SHIFT));
1808c2ecf20Sopenharmony_ci
1818c2ecf20Sopenharmony_ci		/* Reserve 0xfe000000~0xffffffff for RS780E integrated GPU */
1828c2ecf20Sopenharmony_ci		if (node_end_pfn(0) >= (0xffffffff >> PAGE_SHIFT))
1838c2ecf20Sopenharmony_ci			memblock_reserve((node_addrspace_offset | 0xfe000000),
1848c2ecf20Sopenharmony_ci					 32 << 20);
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci		/* Reserve pfn range 0~node[0]->node_start_pfn */
1878c2ecf20Sopenharmony_ci		memblock_reserve(0, PAGE_SIZE * start_pfn);
1888c2ecf20Sopenharmony_ci	}
1898c2ecf20Sopenharmony_ci}
1908c2ecf20Sopenharmony_ci
1918c2ecf20Sopenharmony_cistatic __init void prom_meminit(void)
1928c2ecf20Sopenharmony_ci{
1938c2ecf20Sopenharmony_ci	unsigned int node, cpu, active_cpu = 0;
1948c2ecf20Sopenharmony_ci
1958c2ecf20Sopenharmony_ci	cpu_node_probe();
1968c2ecf20Sopenharmony_ci	init_topology_matrix();
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ci	for (node = 0; node < loongson_sysconf.nr_nodes; node++) {
1998c2ecf20Sopenharmony_ci		if (node_online(node)) {
2008c2ecf20Sopenharmony_ci			szmem(node);
2018c2ecf20Sopenharmony_ci			node_mem_init(node);
2028c2ecf20Sopenharmony_ci			cpumask_clear(&__node_cpumask[node]);
2038c2ecf20Sopenharmony_ci		}
2048c2ecf20Sopenharmony_ci	}
2058c2ecf20Sopenharmony_ci	max_low_pfn = PHYS_PFN(memblock_end_of_DRAM());
2068c2ecf20Sopenharmony_ci
2078c2ecf20Sopenharmony_ci	for (cpu = 0; cpu < loongson_sysconf.nr_cpus; cpu++) {
2088c2ecf20Sopenharmony_ci		node = cpu / loongson_sysconf.cores_per_node;
2098c2ecf20Sopenharmony_ci		if (node >= num_online_nodes())
2108c2ecf20Sopenharmony_ci			node = 0;
2118c2ecf20Sopenharmony_ci
2128c2ecf20Sopenharmony_ci		if (loongson_sysconf.reserved_cpus_mask & (1<<cpu))
2138c2ecf20Sopenharmony_ci			continue;
2148c2ecf20Sopenharmony_ci
2158c2ecf20Sopenharmony_ci		cpumask_set_cpu(active_cpu, &__node_cpumask[node]);
2168c2ecf20Sopenharmony_ci		pr_info("NUMA: set cpumask cpu %d on node %d\n", active_cpu, node);
2178c2ecf20Sopenharmony_ci
2188c2ecf20Sopenharmony_ci		active_cpu++;
2198c2ecf20Sopenharmony_ci	}
2208c2ecf20Sopenharmony_ci}
2218c2ecf20Sopenharmony_ci
2228c2ecf20Sopenharmony_civoid __init paging_init(void)
2238c2ecf20Sopenharmony_ci{
2248c2ecf20Sopenharmony_ci	unsigned long zones_size[MAX_NR_ZONES] = {0, };
2258c2ecf20Sopenharmony_ci
2268c2ecf20Sopenharmony_ci	pagetable_init();
2278c2ecf20Sopenharmony_ci	zones_size[ZONE_DMA32] = MAX_DMA32_PFN;
2288c2ecf20Sopenharmony_ci	zones_size[ZONE_NORMAL] = max_low_pfn;
2298c2ecf20Sopenharmony_ci	free_area_init(zones_size);
2308c2ecf20Sopenharmony_ci}
2318c2ecf20Sopenharmony_ci
2328c2ecf20Sopenharmony_civoid __init mem_init(void)
2338c2ecf20Sopenharmony_ci{
2348c2ecf20Sopenharmony_ci	high_memory = (void *) __va(get_num_physpages() << PAGE_SHIFT);
2358c2ecf20Sopenharmony_ci	memblock_free_all();
2368c2ecf20Sopenharmony_ci	setup_zero_pages();	/* This comes from node 0 */
2378c2ecf20Sopenharmony_ci	mem_init_print_info(NULL);
2388c2ecf20Sopenharmony_ci}
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci/* All PCI device belongs to logical Node-0 */
2418c2ecf20Sopenharmony_ciint pcibus_to_node(struct pci_bus *bus)
2428c2ecf20Sopenharmony_ci{
2438c2ecf20Sopenharmony_ci	return 0;
2448c2ecf20Sopenharmony_ci}
2458c2ecf20Sopenharmony_ciEXPORT_SYMBOL(pcibus_to_node);
2468c2ecf20Sopenharmony_ci
2478c2ecf20Sopenharmony_civoid __init prom_init_numa_memory(void)
2488c2ecf20Sopenharmony_ci{
2498c2ecf20Sopenharmony_ci	enable_lpa();
2508c2ecf20Sopenharmony_ci	prom_meminit();
2518c2ecf20Sopenharmony_ci}
2528c2ecf20Sopenharmony_ciEXPORT_SYMBOL(prom_init_numa_memory);
253