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