Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * This file is subject to the terms and conditions of the GNU General Public
0003  * License.  See the file "COPYING" in the main directory of this archive
0004  * for more details.
0005  *
0006  * Copyright (C) 2000, 05 by Ralf Baechle (ralf@linux-mips.org)
0007  * Copyright (C) 2000 by Silicon Graphics, Inc.
0008  * Copyright (C) 2004 by Christoph Hellwig
0009  *
0010  * On SGI IP27 the ARC memory configuration data is completely bogus but
0011  * alternate easier to use mechanisms are available.
0012  */
0013 #include <linux/init.h>
0014 #include <linux/kernel.h>
0015 #include <linux/memblock.h>
0016 #include <linux/mm.h>
0017 #include <linux/mmzone.h>
0018 #include <linux/export.h>
0019 #include <linux/nodemask.h>
0020 #include <linux/swap.h>
0021 #include <linux/pfn.h>
0022 #include <linux/highmem.h>
0023 #include <asm/page.h>
0024 #include <asm/pgalloc.h>
0025 #include <asm/sections.h>
0026 
0027 #include <asm/sn/arch.h>
0028 #include <asm/sn/agent.h>
0029 #include <asm/sn/klconfig.h>
0030 
0031 #include "ip27-common.h"
0032 
0033 #define SLOT_PFNSHIFT       (SLOT_SHIFT - PAGE_SHIFT)
0034 #define PFN_NASIDSHFT       (NASID_SHFT - PAGE_SHIFT)
0035 
0036 struct node_data *__node_data[MAX_NUMNODES];
0037 
0038 EXPORT_SYMBOL(__node_data);
0039 
0040 static u64 gen_region_mask(void)
0041 {
0042     int region_shift;
0043     u64 region_mask;
0044     nasid_t nasid;
0045 
0046     region_shift = get_region_shift();
0047     region_mask = 0;
0048     for_each_online_node(nasid)
0049         region_mask |= BIT_ULL(nasid >> region_shift);
0050 
0051     return region_mask;
0052 }
0053 
0054 #define rou_rflag   rou_flags
0055 
0056 static int router_distance;
0057 
0058 static void router_recurse(klrou_t *router_a, klrou_t *router_b, int depth)
0059 {
0060     klrou_t *router;
0061     lboard_t *brd;
0062     int port;
0063 
0064     if (router_a->rou_rflag == 1)
0065         return;
0066 
0067     if (depth >= router_distance)
0068         return;
0069 
0070     router_a->rou_rflag = 1;
0071 
0072     for (port = 1; port <= MAX_ROUTER_PORTS; port++) {
0073         if (router_a->rou_port[port].port_nasid == INVALID_NASID)
0074             continue;
0075 
0076         brd = (lboard_t *)NODE_OFFSET_TO_K0(
0077             router_a->rou_port[port].port_nasid,
0078             router_a->rou_port[port].port_offset);
0079 
0080         if (brd->brd_type == KLTYPE_ROUTER) {
0081             router = (klrou_t *)NODE_OFFSET_TO_K0(NASID_GET(brd), brd->brd_compts[0]);
0082             if (router == router_b) {
0083                 if (depth < router_distance)
0084                     router_distance = depth;
0085             }
0086             else
0087                 router_recurse(router, router_b, depth + 1);
0088         }
0089     }
0090 
0091     router_a->rou_rflag = 0;
0092 }
0093 
0094 unsigned char __node_distances[MAX_NUMNODES][MAX_NUMNODES];
0095 EXPORT_SYMBOL(__node_distances);
0096 
0097 static int __init compute_node_distance(nasid_t nasid_a, nasid_t nasid_b)
0098 {
0099     klrou_t *router, *router_a = NULL, *router_b = NULL;
0100     lboard_t *brd, *dest_brd;
0101     nasid_t nasid;
0102     int port;
0103 
0104     /* Figure out which routers nodes in question are connected to */
0105     for_each_online_node(nasid) {
0106         brd = find_lboard_class((lboard_t *)KL_CONFIG_INFO(nasid),
0107                     KLTYPE_ROUTER);
0108 
0109         if (!brd)
0110             continue;
0111 
0112         do {
0113             if (brd->brd_flags & DUPLICATE_BOARD)
0114                 continue;
0115 
0116             router = (klrou_t *)NODE_OFFSET_TO_K0(NASID_GET(brd), brd->brd_compts[0]);
0117             router->rou_rflag = 0;
0118 
0119             for (port = 1; port <= MAX_ROUTER_PORTS; port++) {
0120                 if (router->rou_port[port].port_nasid == INVALID_NASID)
0121                     continue;
0122 
0123                 dest_brd = (lboard_t *)NODE_OFFSET_TO_K0(
0124                     router->rou_port[port].port_nasid,
0125                     router->rou_port[port].port_offset);
0126 
0127                 if (dest_brd->brd_type == KLTYPE_IP27) {
0128                     if (dest_brd->brd_nasid == nasid_a)
0129                         router_a = router;
0130                     if (dest_brd->brd_nasid == nasid_b)
0131                         router_b = router;
0132                 }
0133             }
0134 
0135         } while ((brd = find_lboard_class(KLCF_NEXT(brd), KLTYPE_ROUTER)));
0136     }
0137 
0138     if (nasid_a == nasid_b)
0139         return LOCAL_DISTANCE;
0140 
0141     if (router_a == router_b)
0142         return LOCAL_DISTANCE + 1;
0143 
0144     if (router_a == NULL) {
0145         pr_info("node_distance: router_a NULL\n");
0146         return 255;
0147     }
0148     if (router_b == NULL) {
0149         pr_info("node_distance: router_b NULL\n");
0150         return 255;
0151     }
0152 
0153     router_distance = 100;
0154     router_recurse(router_a, router_b, 2);
0155 
0156     return LOCAL_DISTANCE + router_distance;
0157 }
0158 
0159 static void __init init_topology_matrix(void)
0160 {
0161     nasid_t row, col;
0162 
0163     for (row = 0; row < MAX_NUMNODES; row++)
0164         for (col = 0; col < MAX_NUMNODES; col++)
0165             __node_distances[row][col] = -1;
0166 
0167     for_each_online_node(row) {
0168         for_each_online_node(col) {
0169             __node_distances[row][col] =
0170                 compute_node_distance(row, col);
0171         }
0172     }
0173 }
0174 
0175 static void __init dump_topology(void)
0176 {
0177     nasid_t nasid;
0178     lboard_t *brd, *dest_brd;
0179     int port;
0180     int router_num = 0;
0181     klrou_t *router;
0182     nasid_t row, col;
0183 
0184     pr_info("************** Topology ********************\n");
0185 
0186     pr_info("    ");
0187     for_each_online_node(col)
0188         pr_cont("%02d ", col);
0189     pr_cont("\n");
0190     for_each_online_node(row) {
0191         pr_info("%02d  ", row);
0192         for_each_online_node(col)
0193             pr_cont("%2d ", node_distance(row, col));
0194         pr_cont("\n");
0195     }
0196 
0197     for_each_online_node(nasid) {
0198         brd = find_lboard_class((lboard_t *)KL_CONFIG_INFO(nasid),
0199                     KLTYPE_ROUTER);
0200 
0201         if (!brd)
0202             continue;
0203 
0204         do {
0205             if (brd->brd_flags & DUPLICATE_BOARD)
0206                 continue;
0207             pr_cont("Router %d:", router_num);
0208             router_num++;
0209 
0210             router = (klrou_t *)NODE_OFFSET_TO_K0(NASID_GET(brd), brd->brd_compts[0]);
0211 
0212             for (port = 1; port <= MAX_ROUTER_PORTS; port++) {
0213                 if (router->rou_port[port].port_nasid == INVALID_NASID)
0214                     continue;
0215 
0216                 dest_brd = (lboard_t *)NODE_OFFSET_TO_K0(
0217                     router->rou_port[port].port_nasid,
0218                     router->rou_port[port].port_offset);
0219 
0220                 if (dest_brd->brd_type == KLTYPE_IP27)
0221                     pr_cont(" %d", dest_brd->brd_nasid);
0222                 if (dest_brd->brd_type == KLTYPE_ROUTER)
0223                     pr_cont(" r");
0224             }
0225             pr_cont("\n");
0226 
0227         } while ( (brd = find_lboard_class(KLCF_NEXT(brd), KLTYPE_ROUTER)) );
0228     }
0229 }
0230 
0231 static unsigned long __init slot_getbasepfn(nasid_t nasid, int slot)
0232 {
0233     return ((unsigned long)nasid << PFN_NASIDSHFT) | (slot << SLOT_PFNSHIFT);
0234 }
0235 
0236 static unsigned long __init slot_psize_compute(nasid_t nasid, int slot)
0237 {
0238     lboard_t *brd;
0239     klmembnk_t *banks;
0240     unsigned long size;
0241 
0242     /* Find the node board */
0243     brd = find_lboard((lboard_t *)KL_CONFIG_INFO(nasid), KLTYPE_IP27);
0244     if (!brd)
0245         return 0;
0246 
0247     /* Get the memory bank structure */
0248     banks = (klmembnk_t *) find_first_component(brd, KLSTRUCT_MEMBNK);
0249     if (!banks)
0250         return 0;
0251 
0252     /* Size in _Megabytes_ */
0253     size = (unsigned long)banks->membnk_bnksz[slot/4];
0254 
0255     /* hack for 128 dimm banks */
0256     if (size <= 128) {
0257         if (slot % 4 == 0) {
0258             size <<= 20;        /* size in bytes */
0259             return size >> PAGE_SHIFT;
0260         } else
0261             return 0;
0262     } else {
0263         size /= 4;
0264         size <<= 20;
0265         return size >> PAGE_SHIFT;
0266     }
0267 }
0268 
0269 static void __init mlreset(void)
0270 {
0271     u64 region_mask;
0272     nasid_t nasid;
0273 
0274     master_nasid = get_nasid();
0275 
0276     /*
0277      * Probe for all CPUs - this creates the cpumask and sets up the
0278      * mapping tables.  We need to do this as early as possible.
0279      */
0280 #ifdef CONFIG_SMP
0281     cpu_node_probe();
0282 #endif
0283 
0284     init_topology_matrix();
0285     dump_topology();
0286 
0287     region_mask = gen_region_mask();
0288 
0289     setup_replication_mask();
0290 
0291     /*
0292      * Set all nodes' calias sizes to 8k
0293      */
0294     for_each_online_node(nasid) {
0295         /*
0296          * Always have node 0 in the region mask, otherwise
0297          * CALIAS accesses get exceptions since the hub
0298          * thinks it is a node 0 address.
0299          */
0300         REMOTE_HUB_S(nasid, PI_REGION_PRESENT, (region_mask | 1));
0301         REMOTE_HUB_S(nasid, PI_CALIAS_SIZE, PI_CALIAS_SIZE_0);
0302 
0303 #ifdef LATER
0304         /*
0305          * Set up all hubs to have a big window pointing at
0306          * widget 0. Memory mode, widget 0, offset 0
0307          */
0308         REMOTE_HUB_S(nasid, IIO_ITTE(SWIN0_BIGWIN),
0309             ((HUB_PIO_MAP_TO_MEM << IIO_ITTE_IOSP_SHIFT) |
0310             (0 << IIO_ITTE_WIDGET_SHIFT)));
0311 #endif
0312     }
0313 }
0314 
0315 static void __init szmem(void)
0316 {
0317     unsigned long slot_psize, slot0sz = 0, nodebytes;   /* Hack to detect problem configs */
0318     int slot;
0319     nasid_t node;
0320 
0321     for_each_online_node(node) {
0322         nodebytes = 0;
0323         for (slot = 0; slot < MAX_MEM_SLOTS; slot++) {
0324             slot_psize = slot_psize_compute(node, slot);
0325             if (slot == 0)
0326                 slot0sz = slot_psize;
0327             /*
0328              * We need to refine the hack when we have replicated
0329              * kernel text.
0330              */
0331             nodebytes += (1LL << SLOT_SHIFT);
0332 
0333             if (!slot_psize)
0334                 continue;
0335 
0336             if ((nodebytes >> PAGE_SHIFT) * (sizeof(struct page)) >
0337                         (slot0sz << PAGE_SHIFT)) {
0338                 pr_info("Ignoring slot %d onwards on node %d\n",
0339                                 slot, node);
0340                 slot = MAX_MEM_SLOTS;
0341                 continue;
0342             }
0343             memblock_add_node(PFN_PHYS(slot_getbasepfn(node, slot)),
0344                       PFN_PHYS(slot_psize), node,
0345                       MEMBLOCK_NONE);
0346         }
0347     }
0348 }
0349 
0350 static void __init node_mem_init(nasid_t node)
0351 {
0352     unsigned long slot_firstpfn = slot_getbasepfn(node, 0);
0353     unsigned long slot_freepfn = node_getfirstfree(node);
0354     unsigned long start_pfn, end_pfn;
0355 
0356     get_pfn_range_for_nid(node, &start_pfn, &end_pfn);
0357 
0358     /*
0359      * Allocate the node data structures on the node first.
0360      */
0361     __node_data[node] = __va(slot_freepfn << PAGE_SHIFT);
0362     memset(__node_data[node], 0, PAGE_SIZE);
0363 
0364     NODE_DATA(node)->node_start_pfn = start_pfn;
0365     NODE_DATA(node)->node_spanned_pages = end_pfn - start_pfn;
0366 
0367     cpumask_clear(&hub_data(node)->h_cpus);
0368 
0369     slot_freepfn += PFN_UP(sizeof(struct pglist_data) +
0370                    sizeof(struct hub_data));
0371 
0372     memblock_reserve(slot_firstpfn << PAGE_SHIFT,
0373              ((slot_freepfn - slot_firstpfn) << PAGE_SHIFT));
0374 }
0375 
0376 /*
0377  * A node with nothing.  We use it to avoid any special casing in
0378  * cpumask_of_node
0379  */
0380 static struct node_data null_node = {
0381     .hub = {
0382         .h_cpus = CPU_MASK_NONE
0383     }
0384 };
0385 
0386 /*
0387  * Currently, the intranode memory hole support assumes that each slot
0388  * contains at least 32 MBytes of memory. We assume all bootmem data
0389  * fits on the first slot.
0390  */
0391 void __init prom_meminit(void)
0392 {
0393     nasid_t node;
0394 
0395     mlreset();
0396     szmem();
0397     max_low_pfn = PHYS_PFN(memblock_end_of_DRAM());
0398 
0399     for (node = 0; node < MAX_NUMNODES; node++) {
0400         if (node_online(node)) {
0401             node_mem_init(node);
0402             continue;
0403         }
0404         __node_data[node] = &null_node;
0405     }
0406 }
0407 
0408 extern void setup_zero_pages(void);
0409 
0410 void __init paging_init(void)
0411 {
0412     unsigned long zones_size[MAX_NR_ZONES] = {0, };
0413 
0414     pagetable_init();
0415     zones_size[ZONE_NORMAL] = max_low_pfn;
0416     free_area_init(zones_size);
0417 }
0418 
0419 void __init mem_init(void)
0420 {
0421     high_memory = (void *) __va(get_num_physpages() << PAGE_SHIFT);
0422     memblock_free_all();
0423     setup_zero_pages(); /* This comes from node 0 */
0424 }
0425 
0426 pg_data_t * __init arch_alloc_nodedata(int nid)
0427 {
0428     return memblock_alloc(sizeof(pg_data_t), SMP_CACHE_BYTES);
0429 }
0430 
0431 void arch_refresh_nodedata(int nid, pg_data_t *pgdat)
0432 {
0433     __node_data[nid] = (struct node_data *)pgdat;
0434 }