linux/arch/mips/sgi-ip27/ip27-memory.c
<<
>>
Prefs
   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 */
  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>
  21#include <linux/pfn.h>
  22#include <linux/highmem.h>
  23#include <asm/page.h>
  24#include <asm/pgalloc.h>
  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
  33#define SLOT_PFNSHIFT           (SLOT_SHIFT - PAGE_SHIFT)
  34#define PFN_NASIDSHFT           (NASID_SHFT - PAGE_SHIFT)
  35
  36struct node_data *__node_data[MAX_COMPACT_NODES];
  37
  38EXPORT_SYMBOL(__node_data);
  39
  40static int fine_mode;
  41
  42static 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
  48static 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
  56static hubreg_t region_mask;
  57
  58static 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
  70static int router_distance;
  71
  72static 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
 108unsigned char __node_distances[MAX_COMPACT_NODES][MAX_COMPACT_NODES];
 109
 110static 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
 177static 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
 196static 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
 257static 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
 264static 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
 299static 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(&region_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
 353static void __init szmem(void)
 354{
 355        pfn_t slot_psize, slot0sz = 0, nodebytes;       /* Hack to detect problem configs */
 356        int slot;
 357        cnodeid_t node;
 358
 359        num_physpages = 0;
 360
 361        for_each_online_node(node) {
 362                nodebytes = 0;
 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);
 372
 373                        if (!slot_psize)
 374                                continue;
 375
 376                        if ((nodebytes >> PAGE_SHIFT) * (sizeof(struct page)) >
 377                                                (slot0sz << PAGE_SHIFT)) {
 378                                printk("Ignoring slot %d onwards on node %d\n",
 379                                                                slot, node);
 380                                slot = MAX_MEM_SLOTS;
 381                                continue;
 382                        }
 383                        num_physpages += slot_psize;
 384                        add_active_range(node, slot_getbasepfn(node, slot),
 385                                         slot_getbasepfn(node, slot) + slot_psize);
 386                }
 387        }
 388}
 389
 390static void __init node_mem_init(cnodeid_t node)
 391{
 392        pfn_t slot_firstpfn = slot_getbasepfn(node, 0);
 393        pfn_t slot_freepfn = node_getfirstfree(node);
 394        unsigned long bootmap_size;
 395        pfn_t start_pfn, end_pfn;
 396
 397        get_pfn_range_for_nid(node, &start_pfn, &end_pfn);
 398
 399        /*
 400         * Allocate the node data structures on the node first.
 401         */
 402        __node_data[node] = __va(slot_freepfn << PAGE_SHIFT);
 403
 404        NODE_DATA(node)->bdata = &bootmem_node_data[node];
 405        NODE_DATA(node)->node_start_pfn = start_pfn;
 406        NODE_DATA(node)->node_spanned_pages = end_pfn - start_pfn;
 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,
 414                                        start_pfn, end_pfn);
 415        free_bootmem_with_active_regions(node, end_pfn);
 416        reserve_bootmem_node(NODE_DATA(node), slot_firstpfn << PAGE_SHIFT,
 417                ((slot_freepfn - slot_firstpfn) << PAGE_SHIFT) + bootmap_size,
 418                BOOTMEM_DEFAULT);
 419        sparse_memory_present_with_active_regions(node);
 420}
 421
 422/*
 423 * A node with nothing.  We use it to avoid any special casing in
 424 * cpumask_of_node
 425 */
 426static 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 */
 437void __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
 453void __init prom_free_prom_memory(void)
 454{
 455        /* We got nothing to free here ...  */
 456}
 457
 458extern unsigned long setup_zero_pages(void);
 459
 460void __init paging_init(void)
 461{
 462        unsigned long zones_size[MAX_NR_ZONES] = {0, };
 463        unsigned node;
 464
 465        pagetable_init();
 466
 467        for_each_online_node(node) {
 468                pfn_t start_pfn, end_pfn;
 469
 470                get_pfn_range_for_nid(node, &start_pfn, &end_pfn);
 471
 472                if (end_pfn > max_low_pfn)
 473                        max_low_pfn = end_pfn;
 474        }
 475        zones_size[ZONE_NORMAL] = max_low_pfn;
 476        free_area_init_nodes(zones_size);
 477}
 478
 479void __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) {
 487                /*
 488                 * This will free up the bootmem, ie, slot 0 memory.
 489                 */
 490                totalram_pages += free_all_bootmem_node(NODE_DATA(node));
 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,
 508               (unsigned long) (totalhigh_pages << (PAGE_SHIFT-10)));
 509}
 510