]>
Commit | Line | Data |
---|---|---|
1da177e4 LT |
1 | /* |
2 | * This file is subject to the terms and conditions of the GNU General Public | |
3 | * License. See the file "COPYING" in the main directory of this archive | |
4 | * for more details. | |
5 | * | |
6 | * Copyright (C) 2000, 05 by Ralf Baechle (ralf@linux-mips.org) | |
7 | * Copyright (C) 2000 by Silicon Graphics, Inc. | |
8 | * Copyright (C) 2004 by Christoph Hellwig | |
9 | * | |
10 | * On SGI IP27 the ARC memory configuration data is completly bogus but | |
11 | * alternate easier to use mechanisms are available. | |
12 | */ | |
1da177e4 LT |
13 | #include <linux/init.h> |
14 | #include <linux/kernel.h> | |
15 | #include <linux/mm.h> | |
16 | #include <linux/mmzone.h> | |
17 | #include <linux/module.h> | |
18 | #include <linux/nodemask.h> | |
19 | #include <linux/swap.h> | |
20 | #include <linux/bootmem.h> | |
22a9835c | 21 | #include <linux/pfn.h> |
c1f60a5a | 22 | #include <linux/highmem.h> |
1da177e4 | 23 | #include <asm/page.h> |
6a1e5529 | 24 | #include <asm/pgalloc.h> |
1da177e4 LT |
25 | #include <asm/sections.h> |
26 | ||
27 | #include <asm/sn/arch.h> | |
28 | #include <asm/sn/hub.h> | |
29 | #include <asm/sn/klconfig.h> | |
30 | #include <asm/sn/sn_private.h> | |
31 | ||
32 | ||
1da177e4 LT |
33 | #define SLOT_PFNSHIFT (SLOT_SHIFT - PAGE_SHIFT) |
34 | #define PFN_NASIDSHFT (NASID_SHFT - PAGE_SHIFT) | |
35 | ||
1da177e4 LT |
36 | struct node_data *__node_data[MAX_COMPACT_NODES]; |
37 | ||
38 | EXPORT_SYMBOL(__node_data); | |
39 | ||
40 | static int fine_mode; | |
41 | ||
42 | static int is_fine_dirmode(void) | |
43 | { | |
44 | return (((LOCAL_HUB_L(NI_STATUS_REV_ID) & NSRI_REGIONSIZE_MASK) | |
45 | >> NSRI_REGIONSIZE_SHFT) & REGIONSIZE_FINE); | |
46 | } | |
47 | ||
48 | static hubreg_t get_region(cnodeid_t cnode) | |
49 | { | |
50 | if (fine_mode) | |
51 | return COMPACT_TO_NASID_NODEID(cnode) >> NASID_TO_FINEREG_SHFT; | |
52 | else | |
53 | return COMPACT_TO_NASID_NODEID(cnode) >> NASID_TO_COARSEREG_SHFT; | |
54 | } | |
55 | ||
56 | static hubreg_t region_mask; | |
57 | ||
58 | static void gen_region_mask(hubreg_t *region_mask) | |
59 | { | |
60 | cnodeid_t cnode; | |
61 | ||
62 | (*region_mask) = 0; | |
63 | for_each_online_node(cnode) { | |
64 | (*region_mask) |= 1ULL << get_region(cnode); | |
65 | } | |
66 | } | |
67 | ||
68 | #define rou_rflag rou_flags | |
69 | ||
70 | static int router_distance; | |
71 | ||
72 | static void router_recurse(klrou_t *router_a, klrou_t *router_b, int depth) | |
73 | { | |
74 | klrou_t *router; | |
75 | lboard_t *brd; | |
76 | int port; | |
77 | ||
78 | if (router_a->rou_rflag == 1) | |
79 | return; | |
80 | ||
81 | if (depth >= router_distance) | |
82 | return; | |
83 | ||
84 | router_a->rou_rflag = 1; | |
85 | ||
86 | for (port = 1; port <= MAX_ROUTER_PORTS; port++) { | |
87 | if (router_a->rou_port[port].port_nasid == INVALID_NASID) | |
88 | continue; | |
89 | ||
90 | brd = (lboard_t *)NODE_OFFSET_TO_K0( | |
91 | router_a->rou_port[port].port_nasid, | |
92 | router_a->rou_port[port].port_offset); | |
93 | ||
94 | if (brd->brd_type == KLTYPE_ROUTER) { | |
95 | router = (klrou_t *)NODE_OFFSET_TO_K0(NASID_GET(brd), brd->brd_compts[0]); | |
96 | if (router == router_b) { | |
97 | if (depth < router_distance) | |
98 | router_distance = depth; | |
99 | } | |
100 | else | |
101 | router_recurse(router, router_b, depth + 1); | |
102 | } | |
103 | } | |
104 | ||
105 | router_a->rou_rflag = 0; | |
106 | } | |
107 | ||
108 | unsigned char __node_distances[MAX_COMPACT_NODES][MAX_COMPACT_NODES]; | |
109 | ||
110 | static int __init compute_node_distance(nasid_t nasid_a, nasid_t nasid_b) | |
111 | { | |
112 | klrou_t *router, *router_a = NULL, *router_b = NULL; | |
113 | lboard_t *brd, *dest_brd; | |
114 | cnodeid_t cnode; | |
115 | nasid_t nasid; | |
116 | int port; | |
117 | ||
118 | /* Figure out which routers nodes in question are connected to */ | |
119 | for_each_online_node(cnode) { | |
120 | nasid = COMPACT_TO_NASID_NODEID(cnode); | |
121 | ||
122 | if (nasid == -1) continue; | |
123 | ||
124 | brd = find_lboard_class((lboard_t *)KL_CONFIG_INFO(nasid), | |
125 | KLTYPE_ROUTER); | |
126 | ||
127 | if (!brd) | |
128 | continue; | |
129 | ||
130 | do { | |
131 | if (brd->brd_flags & DUPLICATE_BOARD) | |
132 | continue; | |
133 | ||
134 | router = (klrou_t *)NODE_OFFSET_TO_K0(NASID_GET(brd), brd->brd_compts[0]); | |
135 | router->rou_rflag = 0; | |
136 | ||
137 | for (port = 1; port <= MAX_ROUTER_PORTS; port++) { | |
138 | if (router->rou_port[port].port_nasid == INVALID_NASID) | |
139 | continue; | |
140 | ||
141 | dest_brd = (lboard_t *)NODE_OFFSET_TO_K0( | |
142 | router->rou_port[port].port_nasid, | |
143 | router->rou_port[port].port_offset); | |
144 | ||
145 | if (dest_brd->brd_type == KLTYPE_IP27) { | |
146 | if (dest_brd->brd_nasid == nasid_a) | |
147 | router_a = router; | |
148 | if (dest_brd->brd_nasid == nasid_b) | |
149 | router_b = router; | |
150 | } | |
151 | } | |
152 | ||
153 | } while ((brd = find_lboard_class(KLCF_NEXT(brd), KLTYPE_ROUTER))); | |
154 | } | |
155 | ||
156 | if (router_a == NULL) { | |
157 | printk("node_distance: router_a NULL\n"); | |
158 | return -1; | |
159 | } | |
160 | if (router_b == NULL) { | |
161 | printk("node_distance: router_b NULL\n"); | |
162 | return -1; | |
163 | } | |
164 | ||
165 | if (nasid_a == nasid_b) | |
166 | return 0; | |
167 | ||
168 | if (router_a == router_b) | |
169 | return 1; | |
170 | ||
171 | router_distance = 100; | |
172 | router_recurse(router_a, router_b, 2); | |
173 | ||
174 | return router_distance; | |
175 | } | |
176 | ||
177 | static void __init init_topology_matrix(void) | |
178 | { | |
179 | nasid_t nasid, nasid2; | |
180 | cnodeid_t row, col; | |
181 | ||
182 | for (row = 0; row < MAX_COMPACT_NODES; row++) | |
183 | for (col = 0; col < MAX_COMPACT_NODES; col++) | |
184 | __node_distances[row][col] = -1; | |
185 | ||
186 | for_each_online_node(row) { | |
187 | nasid = COMPACT_TO_NASID_NODEID(row); | |
188 | for_each_online_node(col) { | |
189 | nasid2 = COMPACT_TO_NASID_NODEID(col); | |
190 | __node_distances[row][col] = | |
191 | compute_node_distance(nasid, nasid2); | |
192 | } | |
193 | } | |
194 | } | |
195 | ||
196 | static void __init dump_topology(void) | |
197 | { | |
198 | nasid_t nasid; | |
199 | cnodeid_t cnode; | |
200 | lboard_t *brd, *dest_brd; | |
201 | int port; | |
202 | int router_num = 0; | |
203 | klrou_t *router; | |
204 | cnodeid_t row, col; | |
205 | ||
206 | printk("************** Topology ********************\n"); | |
207 | ||
208 | printk(" "); | |
209 | for_each_online_node(col) | |
210 | printk("%02d ", col); | |
211 | printk("\n"); | |
212 | for_each_online_node(row) { | |
213 | printk("%02d ", row); | |
214 | for_each_online_node(col) | |
215 | printk("%2d ", node_distance(row, col)); | |
216 | printk("\n"); | |
217 | } | |
218 | ||
219 | for_each_online_node(cnode) { | |
220 | nasid = COMPACT_TO_NASID_NODEID(cnode); | |
221 | ||
222 | if (nasid == -1) continue; | |
223 | ||
224 | brd = find_lboard_class((lboard_t *)KL_CONFIG_INFO(nasid), | |
225 | KLTYPE_ROUTER); | |
226 | ||
227 | if (!brd) | |
228 | continue; | |
229 | ||
230 | do { | |
231 | if (brd->brd_flags & DUPLICATE_BOARD) | |
232 | continue; | |
233 | printk("Router %d:", router_num); | |
234 | router_num++; | |
235 | ||
236 | router = (klrou_t *)NODE_OFFSET_TO_K0(NASID_GET(brd), brd->brd_compts[0]); | |
237 | ||
238 | for (port = 1; port <= MAX_ROUTER_PORTS; port++) { | |
239 | if (router->rou_port[port].port_nasid == INVALID_NASID) | |
240 | continue; | |
241 | ||
242 | dest_brd = (lboard_t *)NODE_OFFSET_TO_K0( | |
243 | router->rou_port[port].port_nasid, | |
244 | router->rou_port[port].port_offset); | |
245 | ||
246 | if (dest_brd->brd_type == KLTYPE_IP27) | |
247 | printk(" %d", dest_brd->brd_nasid); | |
248 | if (dest_brd->brd_type == KLTYPE_ROUTER) | |
249 | printk(" r"); | |
250 | } | |
251 | printk("\n"); | |
252 | ||
253 | } while ( (brd = find_lboard_class(KLCF_NEXT(brd), KLTYPE_ROUTER)) ); | |
254 | } | |
255 | } | |
256 | ||
257 | static pfn_t __init slot_getbasepfn(cnodeid_t cnode, int slot) | |
258 | { | |
259 | nasid_t nasid = COMPACT_TO_NASID_NODEID(cnode); | |
260 | ||
261 | return ((pfn_t)nasid << PFN_NASIDSHFT) | (slot << SLOT_PFNSHIFT); | |
262 | } | |
263 | ||
1da177e4 LT |
264 | static pfn_t __init slot_psize_compute(cnodeid_t node, int slot) |
265 | { | |
266 | nasid_t nasid; | |
267 | lboard_t *brd; | |
268 | klmembnk_t *banks; | |
269 | unsigned long size; | |
270 | ||
271 | nasid = COMPACT_TO_NASID_NODEID(node); | |
272 | /* Find the node board */ | |
273 | brd = find_lboard((lboard_t *)KL_CONFIG_INFO(nasid), KLTYPE_IP27); | |
274 | if (!brd) | |
275 | return 0; | |
276 | ||
277 | /* Get the memory bank structure */ | |
278 | banks = (klmembnk_t *) find_first_component(brd, KLSTRUCT_MEMBNK); | |
279 | if (!banks) | |
280 | return 0; | |
281 | ||
282 | /* Size in _Megabytes_ */ | |
283 | size = (unsigned long)banks->membnk_bnksz[slot/4]; | |
284 | ||
285 | /* hack for 128 dimm banks */ | |
286 | if (size <= 128) { | |
287 | if (slot % 4 == 0) { | |
288 | size <<= 20; /* size in bytes */ | |
289 | return(size >> PAGE_SHIFT); | |
290 | } else | |
291 | return 0; | |
292 | } else { | |
293 | size /= 4; | |
294 | size <<= 20; | |
295 | return size >> PAGE_SHIFT; | |
296 | } | |
297 | } | |
298 | ||
299 | static void __init mlreset(void) | |
300 | { | |
301 | int i; | |
302 | ||
303 | master_nasid = get_nasid(); | |
304 | fine_mode = is_fine_dirmode(); | |
305 | ||
306 | /* | |
307 | * Probe for all CPUs - this creates the cpumask and sets up the | |
308 | * mapping tables. We need to do this as early as possible. | |
309 | */ | |
310 | #ifdef CONFIG_SMP | |
311 | cpu_node_probe(); | |
312 | #endif | |
313 | ||
314 | init_topology_matrix(); | |
315 | dump_topology(); | |
316 | ||
317 | gen_region_mask(®ion_mask); | |
318 | ||
319 | setup_replication_mask(); | |
320 | ||
321 | /* | |
322 | * Set all nodes' calias sizes to 8k | |
323 | */ | |
324 | for_each_online_node(i) { | |
325 | nasid_t nasid; | |
326 | ||
327 | nasid = COMPACT_TO_NASID_NODEID(i); | |
328 | ||
329 | /* | |
330 | * Always have node 0 in the region mask, otherwise | |
331 | * CALIAS accesses get exceptions since the hub | |
332 | * thinks it is a node 0 address. | |
333 | */ | |
334 | REMOTE_HUB_S(nasid, PI_REGION_PRESENT, (region_mask | 1)); | |
335 | #ifdef CONFIG_REPLICATE_EXHANDLERS | |
336 | REMOTE_HUB_S(nasid, PI_CALIAS_SIZE, PI_CALIAS_SIZE_8K); | |
337 | #else | |
338 | REMOTE_HUB_S(nasid, PI_CALIAS_SIZE, PI_CALIAS_SIZE_0); | |
339 | #endif | |
340 | ||
341 | #ifdef LATER | |
342 | /* | |
343 | * Set up all hubs to have a big window pointing at | |
344 | * widget 0. Memory mode, widget 0, offset 0 | |
345 | */ | |
346 | REMOTE_HUB_S(nasid, IIO_ITTE(SWIN0_BIGWIN), | |
347 | ((HUB_PIO_MAP_TO_MEM << IIO_ITTE_IOSP_SHIFT) | | |
348 | (0 << IIO_ITTE_WIDGET_SHIFT))); | |
349 | #endif | |
350 | } | |
351 | } | |
352 | ||
353 | static void __init szmem(void) | |
354 | { | |
355 | pfn_t slot_psize, slot0sz = 0, nodebytes; /* Hack to detect problem configs */ | |
2bf8ec2d | 356 | int slot; |
1da177e4 LT |
357 | cnodeid_t node; |
358 | ||
359 | num_physpages = 0; | |
360 | ||
361 | for_each_online_node(node) { | |
2bf8ec2d | 362 | nodebytes = 0; |
1da177e4 LT |
363 | for (slot = 0; slot < MAX_MEM_SLOTS; slot++) { |
364 | slot_psize = slot_psize_compute(node, slot); | |
365 | if (slot == 0) | |
366 | slot0sz = slot_psize; | |
367 | /* | |
368 | * We need to refine the hack when we have replicated | |
369 | * kernel text. | |
370 | */ | |
371 | nodebytes += (1LL << SLOT_SHIFT); | |
2bf8ec2d TB |
372 | |
373 | if (!slot_psize) | |
374 | continue; | |
375 | ||
1da177e4 | 376 | if ((nodebytes >> PAGE_SHIFT) * (sizeof(struct page)) > |
2bf8ec2d | 377 | (slot0sz << PAGE_SHIFT)) { |
1da177e4 LT |
378 | printk("Ignoring slot %d onwards on node %d\n", |
379 | slot, node); | |
1da177e4 LT |
380 | slot = MAX_MEM_SLOTS; |
381 | continue; | |
382 | } | |
383 | num_physpages += slot_psize; | |
2bf8ec2d TB |
384 | add_active_range(node, slot_getbasepfn(node, slot), |
385 | slot_getbasepfn(node, slot) + slot_psize); | |
1da177e4 LT |
386 | } |
387 | } | |
388 | } | |
389 | ||
390 | static void __init node_mem_init(cnodeid_t node) | |
391 | { | |
392 | pfn_t slot_firstpfn = slot_getbasepfn(node, 0); | |
1da177e4 | 393 | pfn_t slot_freepfn = node_getfirstfree(node); |
1da177e4 | 394 | unsigned long bootmap_size; |
2bf8ec2d TB |
395 | pfn_t start_pfn, end_pfn; |
396 | ||
397 | get_pfn_range_for_nid(node, &start_pfn, &end_pfn); | |
1da177e4 LT |
398 | |
399 | /* | |
400 | * Allocate the node data structures on the node first. | |
401 | */ | |
402 | __node_data[node] = __va(slot_freepfn << PAGE_SHIFT); | |
403 | ||
b61bfa3c | 404 | NODE_DATA(node)->bdata = &bootmem_node_data[node]; |
2bf8ec2d TB |
405 | NODE_DATA(node)->node_start_pfn = start_pfn; |
406 | NODE_DATA(node)->node_spanned_pages = end_pfn - start_pfn; | |
1da177e4 LT |
407 | |
408 | cpus_clear(hub_data(node)->h_cpus); | |
409 | ||
410 | slot_freepfn += PFN_UP(sizeof(struct pglist_data) + | |
411 | sizeof(struct hub_data)); | |
412 | ||
413 | bootmap_size = init_bootmem_node(NODE_DATA(node), slot_freepfn, | |
2bf8ec2d TB |
414 | start_pfn, end_pfn); |
415 | free_bootmem_with_active_regions(node, end_pfn); | |
1da177e4 | 416 | reserve_bootmem_node(NODE_DATA(node), slot_firstpfn << PAGE_SHIFT, |
72a7fe39 BW |
417 | ((slot_freepfn - slot_firstpfn) << PAGE_SHIFT) + bootmap_size, |
418 | BOOTMEM_DEFAULT); | |
2bf8ec2d | 419 | sparse_memory_present_with_active_regions(node); |
1da177e4 LT |
420 | } |
421 | ||
422 | /* | |
423 | * A node with nothing. We use it to avoid any special casing in | |
29c337a0 | 424 | * cpumask_of_node |
1da177e4 LT |
425 | */ |
426 | static struct node_data null_node = { | |
427 | .hub = { | |
428 | .h_cpus = CPU_MASK_NONE | |
429 | } | |
430 | }; | |
431 | ||
432 | /* | |
433 | * Currently, the intranode memory hole support assumes that each slot | |
434 | * contains at least 32 MBytes of memory. We assume all bootmem data | |
435 | * fits on the first slot. | |
436 | */ | |
437 | void __init prom_meminit(void) | |
438 | { | |
439 | cnodeid_t node; | |
440 | ||
441 | mlreset(); | |
442 | szmem(); | |
443 | ||
444 | for (node = 0; node < MAX_COMPACT_NODES; node++) { | |
445 | if (node_online(node)) { | |
446 | node_mem_init(node); | |
447 | continue; | |
448 | } | |
449 | __node_data[node] = &null_node; | |
450 | } | |
451 | } | |
452 | ||
c44e8d5e | 453 | void __init prom_free_prom_memory(void) |
1da177e4 LT |
454 | { |
455 | /* We got nothing to free here ... */ | |
1da177e4 LT |
456 | } |
457 | ||
1da177e4 LT |
458 | extern unsigned long setup_zero_pages(void); |
459 | ||
460 | void __init paging_init(void) | |
461 | { | |
f06a9684 | 462 | unsigned long zones_size[MAX_NR_ZONES] = {0, }; |
1da177e4 LT |
463 | unsigned node; |
464 | ||
465 | pagetable_init(); | |
466 | ||
467 | for_each_online_node(node) { | |
2bf8ec2d | 468 | pfn_t start_pfn, end_pfn; |
1da177e4 | 469 | |
2bf8ec2d | 470 | get_pfn_range_for_nid(node, &start_pfn, &end_pfn); |
1da177e4 LT |
471 | |
472 | if (end_pfn > max_low_pfn) | |
473 | max_low_pfn = end_pfn; | |
474 | } | |
2bf8ec2d TB |
475 | zones_size[ZONE_NORMAL] = max_low_pfn; |
476 | free_area_init_nodes(zones_size); | |
1da177e4 LT |
477 | } |
478 | ||
479 | void __init mem_init(void) | |
480 | { | |
481 | unsigned long codesize, datasize, initsize, tmp; | |
482 | unsigned node; | |
483 | ||
484 | high_memory = (void *) __va(num_physpages << PAGE_SHIFT); | |
485 | ||
486 | for_each_online_node(node) { | |
1da177e4 | 487 | /* |
a3dddd56 RB |
488 | * This will free up the bootmem, ie, slot 0 memory. |
489 | */ | |
1da177e4 | 490 | totalram_pages += free_all_bootmem_node(NODE_DATA(node)); |
1da177e4 LT |
491 | } |
492 | ||
493 | totalram_pages -= setup_zero_pages(); /* This comes from node 0 */ | |
494 | ||
495 | codesize = (unsigned long) &_etext - (unsigned long) &_text; | |
496 | datasize = (unsigned long) &_edata - (unsigned long) &_etext; | |
497 | initsize = (unsigned long) &__init_end - (unsigned long) &__init_begin; | |
498 | ||
499 | tmp = nr_free_pages(); | |
500 | printk(KERN_INFO "Memory: %luk/%luk available (%ldk kernel code, " | |
501 | "%ldk reserved, %ldk data, %ldk init, %ldk highmem)\n", | |
502 | tmp << (PAGE_SHIFT-10), | |
503 | num_physpages << (PAGE_SHIFT-10), | |
504 | codesize >> 10, | |
505 | (num_physpages - tmp) << (PAGE_SHIFT-10), | |
506 | datasize >> 10, | |
507 | initsize >> 10, | |
4b529401 | 508 | totalhigh_pages << (PAGE_SHIFT-10)); |
1da177e4 | 509 | } |