]>
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 ([email protected]) | |
7 | * Copyright (C) 2000 by Silicon Graphics, Inc. | |
8 | * Copyright (C) 2004 by Christoph Hellwig | |
9 | * | |
92a76f6d | 10 | * On SGI IP27 the ARC memory configuration data is completely bogus but |
1da177e4 LT |
11 | * alternate easier to use mechanisms are available. |
12 | */ | |
1da177e4 LT |
13 | #include <linux/init.h> |
14 | #include <linux/kernel.h> | |
9d15ffc8 | 15 | #include <linux/memblock.h> |
1da177e4 LT |
16 | #include <linux/mm.h> |
17 | #include <linux/mmzone.h> | |
26dd3e4f | 18 | #include <linux/export.h> |
1da177e4 LT |
19 | #include <linux/nodemask.h> |
20 | #include <linux/swap.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 | ||
70342287 RB |
33 | #define SLOT_PFNSHIFT (SLOT_SHIFT - PAGE_SHIFT) |
34 | #define PFN_NASIDSHFT (NASID_SHFT - PAGE_SHIFT) | |
1da177e4 | 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 | { | |
635c9907 | 44 | return ((LOCAL_HUB_L(NI_STATUS_REV_ID) & NSRI_REGIONSIZE_MASK) >> NSRI_REGIONSIZE_SHFT) & REGIONSIZE_FINE; |
1da177e4 LT |
45 | } |
46 | ||
47 | static hubreg_t get_region(cnodeid_t cnode) | |
48 | { | |
49 | if (fine_mode) | |
50 | return COMPACT_TO_NASID_NODEID(cnode) >> NASID_TO_FINEREG_SHFT; | |
51 | else | |
52 | return COMPACT_TO_NASID_NODEID(cnode) >> NASID_TO_COARSEREG_SHFT; | |
53 | } | |
54 | ||
55 | static hubreg_t region_mask; | |
56 | ||
57 | static void gen_region_mask(hubreg_t *region_mask) | |
58 | { | |
59 | cnodeid_t cnode; | |
60 | ||
61 | (*region_mask) = 0; | |
62 | for_each_online_node(cnode) { | |
63 | (*region_mask) |= 1ULL << get_region(cnode); | |
64 | } | |
65 | } | |
66 | ||
70342287 | 67 | #define rou_rflag rou_flags |
1da177e4 LT |
68 | |
69 | static int router_distance; | |
70 | ||
71 | static void router_recurse(klrou_t *router_a, klrou_t *router_b, int depth) | |
72 | { | |
73 | klrou_t *router; | |
74 | lboard_t *brd; | |
75 | int port; | |
76 | ||
77 | if (router_a->rou_rflag == 1) | |
78 | return; | |
79 | ||
80 | if (depth >= router_distance) | |
81 | return; | |
82 | ||
83 | router_a->rou_rflag = 1; | |
84 | ||
85 | for (port = 1; port <= MAX_ROUTER_PORTS; port++) { | |
86 | if (router_a->rou_port[port].port_nasid == INVALID_NASID) | |
87 | continue; | |
88 | ||
89 | brd = (lboard_t *)NODE_OFFSET_TO_K0( | |
90 | router_a->rou_port[port].port_nasid, | |
91 | router_a->rou_port[port].port_offset); | |
92 | ||
93 | if (brd->brd_type == KLTYPE_ROUTER) { | |
94 | router = (klrou_t *)NODE_OFFSET_TO_K0(NASID_GET(brd), brd->brd_compts[0]); | |
95 | if (router == router_b) { | |
96 | if (depth < router_distance) | |
97 | router_distance = depth; | |
98 | } | |
99 | else | |
100 | router_recurse(router, router_b, depth + 1); | |
101 | } | |
102 | } | |
103 | ||
104 | router_a->rou_rflag = 0; | |
105 | } | |
106 | ||
107 | unsigned char __node_distances[MAX_COMPACT_NODES][MAX_COMPACT_NODES]; | |
5829b0ec | 108 | EXPORT_SYMBOL(__node_distances); |
1da177e4 LT |
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 | ||
fc0460d0 | 257 | static unsigned long __init slot_getbasepfn(cnodeid_t cnode, int slot) |
1da177e4 LT |
258 | { |
259 | nasid_t nasid = COMPACT_TO_NASID_NODEID(cnode); | |
260 | ||
fc0460d0 | 261 | return ((unsigned long)nasid << PFN_NASIDSHFT) | (slot << SLOT_PFNSHIFT); |
1da177e4 LT |
262 | } |
263 | ||
fc0460d0 | 264 | static unsigned long __init slot_psize_compute(cnodeid_t node, int slot) |
1da177e4 LT |
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 */ | |
635c9907 | 289 | return size >> PAGE_SHIFT; |
1da177e4 LT |
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 | { | |
fc0460d0 | 355 | unsigned long slot_psize, slot0sz = 0, nodebytes; /* Hack to detect problem configs */ |
2bf8ec2d | 356 | int slot; |
1da177e4 LT |
357 | cnodeid_t node; |
358 | ||
1da177e4 | 359 | for_each_online_node(node) { |
2bf8ec2d | 360 | nodebytes = 0; |
1da177e4 LT |
361 | for (slot = 0; slot < MAX_MEM_SLOTS; slot++) { |
362 | slot_psize = slot_psize_compute(node, slot); | |
363 | if (slot == 0) | |
364 | slot0sz = slot_psize; | |
365 | /* | |
366 | * We need to refine the hack when we have replicated | |
367 | * kernel text. | |
368 | */ | |
369 | nodebytes += (1LL << SLOT_SHIFT); | |
2bf8ec2d TB |
370 | |
371 | if (!slot_psize) | |
372 | continue; | |
373 | ||
1da177e4 | 374 | if ((nodebytes >> PAGE_SHIFT) * (sizeof(struct page)) > |
2bf8ec2d | 375 | (slot0sz << PAGE_SHIFT)) { |
1da177e4 LT |
376 | printk("Ignoring slot %d onwards on node %d\n", |
377 | slot, node); | |
1da177e4 LT |
378 | slot = MAX_MEM_SLOTS; |
379 | continue; | |
380 | } | |
9d15ffc8 TH |
381 | memblock_add_node(PFN_PHYS(slot_getbasepfn(node, slot)), |
382 | PFN_PHYS(slot_psize), node); | |
1da177e4 LT |
383 | } |
384 | } | |
385 | } | |
386 | ||
387 | static void __init node_mem_init(cnodeid_t node) | |
388 | { | |
fc0460d0 RB |
389 | unsigned long slot_firstpfn = slot_getbasepfn(node, 0); |
390 | unsigned long slot_freepfn = node_getfirstfree(node); | |
fc0460d0 | 391 | unsigned long start_pfn, end_pfn; |
2bf8ec2d TB |
392 | |
393 | get_pfn_range_for_nid(node, &start_pfn, &end_pfn); | |
1da177e4 LT |
394 | |
395 | /* | |
396 | * Allocate the node data structures on the node first. | |
397 | */ | |
398 | __node_data[node] = __va(slot_freepfn << PAGE_SHIFT); | |
93180cec | 399 | memset(__node_data[node], 0, PAGE_SIZE); |
1da177e4 | 400 | |
2bf8ec2d TB |
401 | NODE_DATA(node)->node_start_pfn = start_pfn; |
402 | NODE_DATA(node)->node_spanned_pages = end_pfn - start_pfn; | |
1da177e4 | 403 | |
8dd92891 | 404 | cpumask_clear(&hub_data(node)->h_cpus); |
1da177e4 LT |
405 | |
406 | slot_freepfn += PFN_UP(sizeof(struct pglist_data) + | |
407 | sizeof(struct hub_data)); | |
408 | ||
2bf8ec2d | 409 | free_bootmem_with_active_regions(node, end_pfn); |
bcec54bf MR |
410 | |
411 | memblock_reserve(slot_firstpfn << PAGE_SHIFT, | |
412 | ((slot_freepfn - slot_firstpfn) << PAGE_SHIFT)); | |
413 | ||
2bf8ec2d | 414 | sparse_memory_present_with_active_regions(node); |
1da177e4 LT |
415 | } |
416 | ||
417 | /* | |
70342287 | 418 | * A node with nothing. We use it to avoid any special casing in |
29c337a0 | 419 | * cpumask_of_node |
1da177e4 LT |
420 | */ |
421 | static struct node_data null_node = { | |
422 | .hub = { | |
423 | .h_cpus = CPU_MASK_NONE | |
424 | } | |
425 | }; | |
426 | ||
427 | /* | |
428 | * Currently, the intranode memory hole support assumes that each slot | |
429 | * contains at least 32 MBytes of memory. We assume all bootmem data | |
430 | * fits on the first slot. | |
431 | */ | |
432 | void __init prom_meminit(void) | |
433 | { | |
434 | cnodeid_t node; | |
435 | ||
436 | mlreset(); | |
437 | szmem(); | |
1229ace4 | 438 | max_low_pfn = PHYS_PFN(memblock_end_of_DRAM()); |
1da177e4 LT |
439 | |
440 | for (node = 0; node < MAX_COMPACT_NODES; node++) { | |
441 | if (node_online(node)) { | |
442 | node_mem_init(node); | |
443 | continue; | |
444 | } | |
445 | __node_data[node] = &null_node; | |
446 | } | |
447 | } | |
448 | ||
c44e8d5e | 449 | void __init prom_free_prom_memory(void) |
1da177e4 LT |
450 | { |
451 | /* We got nothing to free here ... */ | |
1da177e4 LT |
452 | } |
453 | ||
31605922 | 454 | extern void setup_zero_pages(void); |
1da177e4 LT |
455 | |
456 | void __init paging_init(void) | |
457 | { | |
f06a9684 | 458 | unsigned long zones_size[MAX_NR_ZONES] = {0, }; |
1da177e4 LT |
459 | |
460 | pagetable_init(); | |
2bf8ec2d TB |
461 | zones_size[ZONE_NORMAL] = max_low_pfn; |
462 | free_area_init_nodes(zones_size); | |
1da177e4 LT |
463 | } |
464 | ||
465 | void __init mem_init(void) | |
466 | { | |
1132137e | 467 | high_memory = (void *) __va(get_num_physpages() << PAGE_SHIFT); |
c6ffc5ca | 468 | memblock_free_all(); |
31605922 | 469 | setup_zero_pages(); /* This comes from node 0 */ |
1132137e | 470 | mem_init_print_info(NULL); |
1da177e4 | 471 | } |