]>
Commit | Line | Data |
---|---|---|
c4617318 HC |
1 | /* |
2 | * Copyright (C) 2010 Loongson Inc. & Lemote Inc. & | |
0bb383a2 | 3 | * Institute of Computing Technology |
c4617318 HC |
4 | * Author: Xiang Gao, gaoxiang@ict.ac.cn |
5 | * Huacai Chen, chenhc@lemote.com | |
6 | * Xiaofu Meng, Shuangshuang Zhang | |
7 | * | |
8 | * This program is free software; you can redistribute it and/or modify it | |
9 | * under the terms of the GNU General Public License as published by the | |
10 | * Free Software Foundation; either version 2 of the License, or (at your | |
11 | * option) any later version. | |
12 | */ | |
13 | #include <linux/init.h> | |
14 | #include <linux/kernel.h> | |
15 | #include <linux/mm.h> | |
16 | #include <linux/mmzone.h> | |
26dd3e4f | 17 | #include <linux/export.h> |
c4617318 HC |
18 | #include <linux/nodemask.h> |
19 | #include <linux/swap.h> | |
20 | #include <linux/memblock.h> | |
21 | #include <linux/bootmem.h> | |
22 | #include <linux/pfn.h> | |
23 | #include <linux/highmem.h> | |
24 | #include <asm/page.h> | |
25 | #include <asm/pgalloc.h> | |
26 | #include <asm/sections.h> | |
c4617318 HC |
27 | #include <linux/irq.h> |
28 | #include <asm/bootinfo.h> | |
29 | #include <asm/mc146818-time.h> | |
30 | #include <asm/time.h> | |
31 | #include <asm/wbflush.h> | |
32 | #include <boot_param.h> | |
33 | ||
34 | static struct node_data prealloc__node_data[MAX_NUMNODES]; | |
35 | unsigned char __node_distances[MAX_NUMNODES][MAX_NUMNODES]; | |
21255dad | 36 | EXPORT_SYMBOL(__node_distances); |
c4617318 HC |
37 | struct node_data *__node_data[MAX_NUMNODES]; |
38 | EXPORT_SYMBOL(__node_data); | |
39 | ||
40 | static void enable_lpa(void) | |
41 | { | |
42 | unsigned long value; | |
43 | ||
44 | value = __read_32bit_c0_register($16, 3); | |
45 | value |= 0x00000080; | |
46 | __write_32bit_c0_register($16, 3, value); | |
47 | value = __read_32bit_c0_register($16, 3); | |
48 | pr_info("CP0_Config3: CP0 16.3 (0x%lx)\n", value); | |
49 | ||
50 | value = __read_32bit_c0_register($5, 1); | |
51 | value |= 0x20000000; | |
52 | __write_32bit_c0_register($5, 1, value); | |
53 | value = __read_32bit_c0_register($5, 1); | |
54 | pr_info("CP0_PageGrain: CP0 5.1 (0x%lx)\n", value); | |
55 | } | |
56 | ||
57 | static void cpu_node_probe(void) | |
58 | { | |
59 | int i; | |
60 | ||
61 | nodes_clear(node_possible_map); | |
62 | nodes_clear(node_online_map); | |
63 | for (i = 0; i < loongson_sysconf.nr_nodes; i++) { | |
64 | node_set_state(num_online_nodes(), N_POSSIBLE); | |
65 | node_set_online(num_online_nodes()); | |
66 | } | |
67 | ||
68 | pr_info("NUMA: Discovered %d cpus on %d nodes\n", | |
69 | loongson_sysconf.nr_cpus, num_online_nodes()); | |
70 | } | |
71 | ||
72 | static int __init compute_node_distance(int row, int col) | |
73 | { | |
74 | int package_row = row * loongson_sysconf.cores_per_node / | |
75 | loongson_sysconf.cores_per_package; | |
76 | int package_col = col * loongson_sysconf.cores_per_node / | |
77 | loongson_sysconf.cores_per_package; | |
78 | ||
79 | if (col == row) | |
80 | return 0; | |
81 | else if (package_row == package_col) | |
82 | return 40; | |
83 | else | |
84 | return 100; | |
85 | } | |
86 | ||
87 | static void __init init_topology_matrix(void) | |
88 | { | |
89 | int row, col; | |
90 | ||
91 | for (row = 0; row < MAX_NUMNODES; row++) | |
92 | for (col = 0; col < MAX_NUMNODES; col++) | |
93 | __node_distances[row][col] = -1; | |
94 | ||
95 | for_each_online_node(row) { | |
96 | for_each_online_node(col) { | |
97 | __node_distances[row][col] = | |
98 | compute_node_distance(row, col); | |
99 | } | |
100 | } | |
101 | } | |
102 | ||
103 | static unsigned long nid_to_addroffset(unsigned int nid) | |
104 | { | |
105 | unsigned long result; | |
106 | switch (nid) { | |
107 | case 0: | |
108 | default: | |
109 | result = NODE0_ADDRSPACE_OFFSET; | |
110 | break; | |
111 | case 1: | |
112 | result = NODE1_ADDRSPACE_OFFSET; | |
113 | break; | |
114 | case 2: | |
115 | result = NODE2_ADDRSPACE_OFFSET; | |
116 | break; | |
117 | case 3: | |
118 | result = NODE3_ADDRSPACE_OFFSET; | |
119 | break; | |
120 | } | |
121 | return result; | |
122 | } | |
123 | ||
124 | static void __init szmem(unsigned int node) | |
125 | { | |
126 | u32 i, mem_type; | |
127 | static unsigned long num_physpages = 0; | |
128 | u64 node_id, node_psize, start_pfn, end_pfn, mem_start, mem_size; | |
129 | ||
130 | /* Parse memory information and activate */ | |
131 | for (i = 0; i < loongson_memmap->nr_map; i++) { | |
132 | node_id = loongson_memmap->map[i].node_id; | |
133 | if (node_id != node) | |
134 | continue; | |
135 | ||
136 | mem_type = loongson_memmap->map[i].mem_type; | |
137 | mem_size = loongson_memmap->map[i].mem_size; | |
138 | mem_start = loongson_memmap->map[i].mem_start; | |
139 | ||
140 | switch (mem_type) { | |
141 | case SYSTEM_RAM_LOW: | |
142 | start_pfn = ((node_id << 44) + mem_start) >> PAGE_SHIFT; | |
143 | node_psize = (mem_size << 20) >> PAGE_SHIFT; | |
144 | end_pfn = start_pfn + node_psize; | |
145 | num_physpages += node_psize; | |
146 | pr_info("Node%d: mem_type:%d, mem_start:0x%llx, mem_size:0x%llx MB\n", | |
147 | (u32)node_id, mem_type, mem_start, mem_size); | |
148 | pr_info(" start_pfn:0x%llx, end_pfn:0x%llx, num_physpages:0x%lx\n", | |
149 | start_pfn, end_pfn, num_physpages); | |
150 | add_memory_region((node_id << 44) + mem_start, | |
151 | (u64)mem_size << 20, BOOT_MEM_RAM); | |
152 | memblock_add_node(PFN_PHYS(start_pfn), | |
153 | PFN_PHYS(end_pfn - start_pfn), node); | |
154 | break; | |
155 | case SYSTEM_RAM_HIGH: | |
156 | start_pfn = ((node_id << 44) + mem_start) >> PAGE_SHIFT; | |
157 | node_psize = (mem_size << 20) >> PAGE_SHIFT; | |
158 | end_pfn = start_pfn + node_psize; | |
159 | num_physpages += node_psize; | |
160 | pr_info("Node%d: mem_type:%d, mem_start:0x%llx, mem_size:0x%llx MB\n", | |
161 | (u32)node_id, mem_type, mem_start, mem_size); | |
162 | pr_info(" start_pfn:0x%llx, end_pfn:0x%llx, num_physpages:0x%lx\n", | |
163 | start_pfn, end_pfn, num_physpages); | |
164 | add_memory_region((node_id << 44) + mem_start, | |
165 | (u64)mem_size << 20, BOOT_MEM_RAM); | |
166 | memblock_add_node(PFN_PHYS(start_pfn), | |
167 | PFN_PHYS(end_pfn - start_pfn), node); | |
168 | break; | |
6045f241 | 169 | case SYSTEM_RAM_RESERVED: |
c4617318 HC |
170 | pr_info("Node%d: mem_type:%d, mem_start:0x%llx, mem_size:0x%llx MB\n", |
171 | (u32)node_id, mem_type, mem_start, mem_size); | |
172 | add_memory_region((node_id << 44) + mem_start, | |
173 | (u64)mem_size << 20, BOOT_MEM_RESERVED); | |
174 | memblock_reserve(((node_id << 44) + mem_start), | |
175 | mem_size << 20); | |
176 | break; | |
177 | } | |
178 | } | |
179 | } | |
180 | ||
181 | static void __init node_mem_init(unsigned int node) | |
182 | { | |
183 | unsigned long bootmap_size; | |
184 | unsigned long node_addrspace_offset; | |
185 | unsigned long start_pfn, end_pfn, freepfn; | |
186 | ||
187 | node_addrspace_offset = nid_to_addroffset(node); | |
188 | pr_info("Node%d's addrspace_offset is 0x%lx\n", | |
189 | node, node_addrspace_offset); | |
190 | ||
191 | get_pfn_range_for_nid(node, &start_pfn, &end_pfn); | |
192 | freepfn = start_pfn; | |
193 | if (node == 0) | |
194 | freepfn = PFN_UP(__pa_symbol(&_end)); /* kernel end address */ | |
195 | pr_info("Node%d: start_pfn=0x%lx, end_pfn=0x%lx, freepfn=0x%lx\n", | |
196 | node, start_pfn, end_pfn, freepfn); | |
197 | ||
198 | __node_data[node] = prealloc__node_data + node; | |
199 | ||
200 | NODE_DATA(node)->bdata = &bootmem_node_data[node]; | |
201 | NODE_DATA(node)->node_start_pfn = start_pfn; | |
202 | NODE_DATA(node)->node_spanned_pages = end_pfn - start_pfn; | |
203 | ||
204 | bootmap_size = init_bootmem_node(NODE_DATA(node), freepfn, | |
205 | start_pfn, end_pfn); | |
206 | free_bootmem_with_active_regions(node, end_pfn); | |
207 | if (node == 0) /* used by finalize_initrd() */ | |
208 | max_low_pfn = end_pfn; | |
209 | ||
210 | /* This is reserved for the kernel and bdata->node_bootmem_map */ | |
211 | reserve_bootmem_node(NODE_DATA(node), start_pfn << PAGE_SHIFT, | |
212 | ((freepfn - start_pfn) << PAGE_SHIFT) + bootmap_size, | |
213 | BOOTMEM_DEFAULT); | |
214 | ||
215 | if (node == 0 && node_end_pfn(0) >= (0xffffffff >> PAGE_SHIFT)) { | |
3484de7b | 216 | /* Reserve 0xfe000000~0xffffffff for RS780E integrated GPU */ |
c4617318 | 217 | reserve_bootmem_node(NODE_DATA(node), |
3484de7b HC |
218 | (node_addrspace_offset | 0xfe000000), |
219 | 32 << 20, BOOTMEM_DEFAULT); | |
c4617318 HC |
220 | } |
221 | ||
222 | sparse_memory_present_with_active_regions(node); | |
223 | } | |
224 | ||
225 | static __init void prom_meminit(void) | |
226 | { | |
ec0f8d3f | 227 | unsigned int node, cpu, active_cpu = 0; |
c4617318 HC |
228 | |
229 | cpu_node_probe(); | |
230 | init_topology_matrix(); | |
231 | ||
232 | for (node = 0; node < loongson_sysconf.nr_nodes; node++) { | |
233 | if (node_online(node)) { | |
234 | szmem(node); | |
235 | node_mem_init(node); | |
8dd92891 | 236 | cpumask_clear(&__node_data[(node)]->cpumask); |
c4617318 HC |
237 | } |
238 | } | |
239 | for (cpu = 0; cpu < loongson_sysconf.nr_cpus; cpu++) { | |
240 | node = cpu / loongson_sysconf.cores_per_node; | |
241 | if (node >= num_online_nodes()) | |
242 | node = 0; | |
ec0f8d3f HC |
243 | |
244 | if (loongson_sysconf.reserved_cpus_mask & (1<<cpu)) | |
245 | continue; | |
246 | ||
8dd92891 | 247 | cpumask_set_cpu(active_cpu, &__node_data[(node)]->cpumask); |
ec0f8d3f HC |
248 | pr_info("NUMA: set cpumask cpu %d on node %d\n", active_cpu, node); |
249 | ||
250 | active_cpu++; | |
c4617318 HC |
251 | } |
252 | } | |
253 | ||
254 | void __init paging_init(void) | |
255 | { | |
256 | unsigned node; | |
257 | unsigned long zones_size[MAX_NR_ZONES] = {0, }; | |
258 | ||
259 | pagetable_init(); | |
260 | ||
261 | for_each_online_node(node) { | |
262 | unsigned long start_pfn, end_pfn; | |
263 | ||
264 | get_pfn_range_for_nid(node, &start_pfn, &end_pfn); | |
265 | ||
266 | if (end_pfn > max_low_pfn) | |
267 | max_low_pfn = end_pfn; | |
268 | } | |
269 | #ifdef CONFIG_ZONE_DMA32 | |
270 | zones_size[ZONE_DMA32] = MAX_DMA32_PFN; | |
271 | #endif | |
272 | zones_size[ZONE_NORMAL] = max_low_pfn; | |
273 | free_area_init_nodes(zones_size); | |
274 | } | |
275 | ||
276 | void __init mem_init(void) | |
277 | { | |
278 | high_memory = (void *) __va(get_num_physpages() << PAGE_SHIFT); | |
279 | free_all_bootmem(); | |
280 | setup_zero_pages(); /* This comes from node 0 */ | |
281 | mem_init_print_info(NULL); | |
282 | } | |
283 | ||
284 | /* All PCI device belongs to logical Node-0 */ | |
285 | int pcibus_to_node(struct pci_bus *bus) | |
286 | { | |
287 | return 0; | |
288 | } | |
289 | EXPORT_SYMBOL(pcibus_to_node); | |
290 | ||
291 | void __init prom_init_numa_memory(void) | |
292 | { | |
293 | enable_lpa(); | |
294 | prom_meminit(); | |
295 | } | |
296 | EXPORT_SYMBOL(prom_init_numa_memory); |