2005-04-16 15:20:36 -07:00
/*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file " COPYING " in the main directory of this archive
* for more details .
*
* Copyright ( C ) 2000 , 05 by Ralf Baechle ( ralf @ linux - mips . org )
* Copyright ( C ) 2000 by Silicon Graphics , Inc .
* Copyright ( C ) 2004 by Christoph Hellwig
*
2016-02-25 00:44:58 -08:00
* On SGI IP27 the ARC memory configuration data is completely bogus but
2005-04-16 15:20:36 -07:00
* alternate easier to use mechanisms are available .
*/
# include <linux/init.h>
# include <linux/kernel.h>
2011-12-08 10:22:09 -08:00
# include <linux/memblock.h>
2005-04-16 15:20:36 -07:00
# include <linux/mm.h>
# include <linux/mmzone.h>
2017-01-28 21:05:57 -05:00
# include <linux/export.h>
2005-04-16 15:20:36 -07:00
# include <linux/nodemask.h>
# include <linux/swap.h>
2006-03-27 01:16:04 -08:00
# include <linux/pfn.h>
2006-09-25 23:31:11 -07:00
# include <linux/highmem.h>
2005-04-16 15:20:36 -07:00
# include <asm/page.h>
2007-02-19 01:27:34 +09:00
# include <asm/pgalloc.h>
2005-04-16 15:20:36 -07:00
# include <asm/sections.h>
# include <asm/sn/arch.h>
2020-01-09 13:33:47 +01:00
# include <asm/sn/agent.h>
2005-04-16 15:20:36 -07:00
# include <asm/sn/klconfig.h>
2020-01-09 13:33:48 +01:00
# include "ip27-common.h"
2005-04-16 15:20:36 -07:00
2013-01-22 12:59:30 +01:00
# define SLOT_PFNSHIFT (SLOT_SHIFT - PAGE_SHIFT)
# define PFN_NASIDSHFT (NASID_SHFT - PAGE_SHIFT)
2005-04-16 15:20:36 -07:00
2019-10-30 11:51:44 +01:00
struct node_data * __node_data [ MAX_NUMNODES ] ;
2005-04-16 15:20:36 -07:00
EXPORT_SYMBOL ( __node_data ) ;
2020-01-09 13:33:47 +01:00
static u64 gen_region_mask ( void )
2005-04-16 15:20:36 -07:00
{
2020-01-09 13:33:47 +01:00
int region_shift ;
u64 region_mask ;
2019-10-03 14:27:24 +02:00
nasid_t nasid ;
2005-04-16 15:20:36 -07:00
2020-01-09 13:33:47 +01:00
region_shift = get_region_shift ( ) ;
region_mask = 0 ;
for_each_online_node ( nasid )
region_mask | = BIT_ULL ( nasid > > region_shift ) ;
return region_mask ;
2005-04-16 15:20:36 -07:00
}
2013-01-22 12:59:30 +01:00
# define rou_rflag rou_flags
2005-04-16 15:20:36 -07:00
static int router_distance ;
static void router_recurse ( klrou_t * router_a , klrou_t * router_b , int depth )
{
klrou_t * router ;
lboard_t * brd ;
int port ;
if ( router_a - > rou_rflag = = 1 )
return ;
if ( depth > = router_distance )
return ;
router_a - > rou_rflag = 1 ;
for ( port = 1 ; port < = MAX_ROUTER_PORTS ; port + + ) {
if ( router_a - > rou_port [ port ] . port_nasid = = INVALID_NASID )
continue ;
brd = ( lboard_t * ) NODE_OFFSET_TO_K0 (
router_a - > rou_port [ port ] . port_nasid ,
router_a - > rou_port [ port ] . port_offset ) ;
if ( brd - > brd_type = = KLTYPE_ROUTER ) {
router = ( klrou_t * ) NODE_OFFSET_TO_K0 ( NASID_GET ( brd ) , brd - > brd_compts [ 0 ] ) ;
if ( router = = router_b ) {
if ( depth < router_distance )
router_distance = depth ;
}
else
router_recurse ( router , router_b , depth + 1 ) ;
}
}
router_a - > rou_rflag = 0 ;
}
2019-10-30 11:51:44 +01:00
unsigned char __node_distances [ MAX_NUMNODES ] [ MAX_NUMNODES ] ;
2014-11-13 11:08:07 +00:00
EXPORT_SYMBOL ( __node_distances ) ;
2005-04-16 15:20:36 -07:00
static int __init compute_node_distance ( nasid_t nasid_a , nasid_t nasid_b )
{
klrou_t * router , * router_a = NULL , * router_b = NULL ;
lboard_t * brd , * dest_brd ;
nasid_t nasid ;
int port ;
/* Figure out which routers nodes in question are connected to */
2019-10-03 14:27:24 +02:00
for_each_online_node ( nasid ) {
2005-04-16 15:20:36 -07:00
brd = find_lboard_class ( ( lboard_t * ) KL_CONFIG_INFO ( nasid ) ,
KLTYPE_ROUTER ) ;
if ( ! brd )
continue ;
do {
if ( brd - > brd_flags & DUPLICATE_BOARD )
continue ;
router = ( klrou_t * ) NODE_OFFSET_TO_K0 ( NASID_GET ( brd ) , brd - > brd_compts [ 0 ] ) ;
router - > rou_rflag = 0 ;
for ( port = 1 ; port < = MAX_ROUTER_PORTS ; port + + ) {
if ( router - > rou_port [ port ] . port_nasid = = INVALID_NASID )
continue ;
dest_brd = ( lboard_t * ) NODE_OFFSET_TO_K0 (
router - > rou_port [ port ] . port_nasid ,
router - > rou_port [ port ] . port_offset ) ;
if ( dest_brd - > brd_type = = KLTYPE_IP27 ) {
if ( dest_brd - > brd_nasid = = nasid_a )
router_a = router ;
if ( dest_brd - > brd_nasid = = nasid_b )
router_b = router ;
}
}
} while ( ( brd = find_lboard_class ( KLCF_NEXT ( brd ) , KLTYPE_ROUTER ) ) ) ;
}
2020-01-09 13:23:29 +01:00
if ( nasid_a = = nasid_b )
return LOCAL_DISTANCE ;
if ( router_a = = router_b )
return LOCAL_DISTANCE + 1 ;
2005-04-16 15:20:36 -07:00
if ( router_a = = NULL ) {
2019-02-19 16:57:17 +01:00
pr_info ( " node_distance: router_a NULL \n " ) ;
2020-01-09 13:23:29 +01:00
return 255 ;
2005-04-16 15:20:36 -07:00
}
if ( router_b = = NULL ) {
2019-02-19 16:57:17 +01:00
pr_info ( " node_distance: router_b NULL \n " ) ;
2020-01-09 13:23:29 +01:00
return 255 ;
2005-04-16 15:20:36 -07:00
}
router_distance = 100 ;
router_recurse ( router_a , router_b , 2 ) ;
2020-01-09 13:23:29 +01:00
return LOCAL_DISTANCE + router_distance ;
2005-04-16 15:20:36 -07:00
}
static void __init init_topology_matrix ( void )
{
2019-10-03 14:27:24 +02:00
nasid_t row , col ;
2005-04-16 15:20:36 -07:00
2019-10-30 11:51:44 +01:00
for ( row = 0 ; row < MAX_NUMNODES ; row + + )
for ( col = 0 ; col < MAX_NUMNODES ; col + + )
2005-04-16 15:20:36 -07:00
__node_distances [ row ] [ col ] = - 1 ;
for_each_online_node ( row ) {
for_each_online_node ( col ) {
__node_distances [ row ] [ col ] =
2019-10-03 14:27:24 +02:00
compute_node_distance ( row , col ) ;
2005-04-16 15:20:36 -07:00
}
}
}
static void __init dump_topology ( void )
{
nasid_t nasid ;
lboard_t * brd , * dest_brd ;
int port ;
int router_num = 0 ;
klrou_t * router ;
2019-10-03 14:27:24 +02:00
nasid_t row , col ;
2005-04-16 15:20:36 -07:00
2019-02-19 16:57:17 +01:00
pr_info ( " ************** Topology ******************** \n " ) ;
2005-04-16 15:20:36 -07:00
2019-02-19 16:57:17 +01:00
pr_info ( " " ) ;
2005-04-16 15:20:36 -07:00
for_each_online_node ( col )
2019-02-19 16:57:17 +01:00
pr_cont ( " %02d " , col ) ;
pr_cont ( " \n " ) ;
2005-04-16 15:20:36 -07:00
for_each_online_node ( row ) {
2019-02-19 16:57:17 +01:00
pr_info ( " %02d " , row ) ;
2005-04-16 15:20:36 -07:00
for_each_online_node ( col )
2019-02-19 16:57:17 +01:00
pr_cont ( " %2d " , node_distance ( row , col ) ) ;
pr_cont ( " \n " ) ;
2005-04-16 15:20:36 -07:00
}
2019-10-03 14:27:24 +02:00
for_each_online_node ( nasid ) {
2005-04-16 15:20:36 -07:00
brd = find_lboard_class ( ( lboard_t * ) KL_CONFIG_INFO ( nasid ) ,
KLTYPE_ROUTER ) ;
if ( ! brd )
continue ;
do {
if ( brd - > brd_flags & DUPLICATE_BOARD )
continue ;
2019-02-19 16:57:17 +01:00
pr_cont ( " Router %d: " , router_num ) ;
2005-04-16 15:20:36 -07:00
router_num + + ;
router = ( klrou_t * ) NODE_OFFSET_TO_K0 ( NASID_GET ( brd ) , brd - > brd_compts [ 0 ] ) ;
for ( port = 1 ; port < = MAX_ROUTER_PORTS ; port + + ) {
if ( router - > rou_port [ port ] . port_nasid = = INVALID_NASID )
continue ;
dest_brd = ( lboard_t * ) NODE_OFFSET_TO_K0 (
router - > rou_port [ port ] . port_nasid ,
router - > rou_port [ port ] . port_offset ) ;
if ( dest_brd - > brd_type = = KLTYPE_IP27 )
2019-02-19 16:57:17 +01:00
pr_cont ( " %d " , dest_brd - > brd_nasid ) ;
2005-04-16 15:20:36 -07:00
if ( dest_brd - > brd_type = = KLTYPE_ROUTER )
2019-02-19 16:57:17 +01:00
pr_cont ( " r " ) ;
2005-04-16 15:20:36 -07:00
}
2019-02-19 16:57:17 +01:00
pr_cont ( " \n " ) ;
2005-04-16 15:20:36 -07:00
} while ( ( brd = find_lboard_class ( KLCF_NEXT ( brd ) , KLTYPE_ROUTER ) ) ) ;
}
}
2019-10-03 14:27:24 +02:00
static unsigned long __init slot_getbasepfn ( nasid_t nasid , int slot )
2005-04-16 15:20:36 -07:00
{
2013-05-08 03:51:58 +02:00
return ( ( unsigned long ) nasid < < PFN_NASIDSHFT ) | ( slot < < SLOT_PFNSHIFT ) ;
2005-04-16 15:20:36 -07:00
}
2019-10-03 14:27:24 +02:00
static unsigned long __init slot_psize_compute ( nasid_t nasid , int slot )
2005-04-16 15:20:36 -07:00
{
lboard_t * brd ;
klmembnk_t * banks ;
unsigned long size ;
/* Find the node board */
brd = find_lboard ( ( lboard_t * ) KL_CONFIG_INFO ( nasid ) , KLTYPE_IP27 ) ;
if ( ! brd )
return 0 ;
/* Get the memory bank structure */
banks = ( klmembnk_t * ) find_first_component ( brd , KLSTRUCT_MEMBNK ) ;
if ( ! banks )
return 0 ;
/* Size in _Megabytes_ */
size = ( unsigned long ) banks - > membnk_bnksz [ slot / 4 ] ;
/* hack for 128 dimm banks */
if ( size < = 128 ) {
if ( slot % 4 = = 0 ) {
size < < = 20 ; /* size in bytes */
2014-10-21 14:12:49 +02:00
return size > > PAGE_SHIFT ;
2005-04-16 15:20:36 -07:00
} else
return 0 ;
} else {
size / = 4 ;
size < < = 20 ;
return size > > PAGE_SHIFT ;
}
}
static void __init mlreset ( void )
{
2020-01-09 13:33:47 +01:00
u64 region_mask ;
2019-10-03 14:27:24 +02:00
nasid_t nasid ;
2005-04-16 15:20:36 -07:00
master_nasid = get_nasid ( ) ;
/*
* Probe for all CPUs - this creates the cpumask and sets up the
* mapping tables . We need to do this as early as possible .
*/
# ifdef CONFIG_SMP
cpu_node_probe ( ) ;
# endif
init_topology_matrix ( ) ;
dump_topology ( ) ;
2020-01-09 13:33:47 +01:00
region_mask = gen_region_mask ( ) ;
2005-04-16 15:20:36 -07:00
setup_replication_mask ( ) ;
/*
* Set all nodes ' calias sizes to 8 k
*/
2019-10-03 14:27:24 +02:00
for_each_online_node ( nasid ) {
2005-04-16 15:20:36 -07:00
/*
* Always have node 0 in the region mask , otherwise
* CALIAS accesses get exceptions since the hub
* thinks it is a node 0 address .
*/
REMOTE_HUB_S ( nasid , PI_REGION_PRESENT , ( region_mask | 1 ) ) ;
REMOTE_HUB_S ( nasid , PI_CALIAS_SIZE , PI_CALIAS_SIZE_0 ) ;
# ifdef LATER
/*
* Set up all hubs to have a big window pointing at
* widget 0. Memory mode , widget 0 , offset 0
*/
REMOTE_HUB_S ( nasid , IIO_ITTE ( SWIN0_BIGWIN ) ,
( ( HUB_PIO_MAP_TO_MEM < < IIO_ITTE_IOSP_SHIFT ) |
( 0 < < IIO_ITTE_WIDGET_SHIFT ) ) ) ;
# endif
}
}
static void __init szmem ( void )
{
2013-05-08 03:51:58 +02:00
unsigned long slot_psize , slot0sz = 0 , nodebytes ; /* Hack to detect problem configs */
2008-04-08 23:43:46 +02:00
int slot ;
2019-10-03 14:27:24 +02:00
nasid_t node ;
2005-04-16 15:20:36 -07:00
for_each_online_node ( node ) {
2008-04-08 23:43:46 +02:00
nodebytes = 0 ;
2005-04-16 15:20:36 -07:00
for ( slot = 0 ; slot < MAX_MEM_SLOTS ; slot + + ) {
slot_psize = slot_psize_compute ( node , slot ) ;
if ( slot = = 0 )
slot0sz = slot_psize ;
/*
* We need to refine the hack when we have replicated
* kernel text .
*/
nodebytes + = ( 1LL < < SLOT_SHIFT ) ;
2008-04-08 23:43:46 +02:00
if ( ! slot_psize )
continue ;
2005-04-16 15:20:36 -07:00
if ( ( nodebytes > > PAGE_SHIFT ) * ( sizeof ( struct page ) ) >
2008-04-08 23:43:46 +02:00
( slot0sz < < PAGE_SHIFT ) ) {
2019-02-19 16:57:17 +01:00
pr_info ( " Ignoring slot %d onwards on node %d \n " ,
2005-04-16 15:20:36 -07:00
slot , node ) ;
slot = MAX_MEM_SLOTS ;
continue ;
}
2011-12-08 10:22:09 -08:00
memblock_add_node ( PFN_PHYS ( slot_getbasepfn ( node , slot ) ) ,
PFN_PHYS ( slot_psize ) , node ) ;
2005-04-16 15:20:36 -07:00
}
}
}
2019-10-03 14:27:24 +02:00
static void __init node_mem_init ( nasid_t node )
2005-04-16 15:20:36 -07:00
{
2013-05-08 03:51:58 +02:00
unsigned long slot_firstpfn = slot_getbasepfn ( node , 0 ) ;
unsigned long slot_freepfn = node_getfirstfree ( node ) ;
unsigned long start_pfn , end_pfn ;
2008-04-08 23:43:46 +02:00
get_pfn_range_for_nid ( node , & start_pfn , & end_pfn ) ;
2005-04-16 15:20:36 -07:00
/*
* Allocate the node data structures on the node first .
*/
__node_data [ node ] = __va ( slot_freepfn < < PAGE_SHIFT ) ;
2012-07-31 16:46:11 -07:00
memset ( __node_data [ node ] , 0 , PAGE_SIZE ) ;
2005-04-16 15:20:36 -07:00
2008-04-08 23:43:46 +02:00
NODE_DATA ( node ) - > node_start_pfn = start_pfn ;
NODE_DATA ( node ) - > node_spanned_pages = end_pfn - start_pfn ;
2005-04-16 15:20:36 -07:00
2015-03-05 10:49:17 +10:30
cpumask_clear ( & hub_data ( node ) - > h_cpus ) ;
2005-04-16 15:20:36 -07:00
slot_freepfn + = PFN_UP ( sizeof ( struct pglist_data ) +
sizeof ( struct hub_data ) ) ;
2018-09-10 12:23:18 +03:00
memblock_reserve ( slot_firstpfn < < PAGE_SHIFT ,
( ( slot_freepfn - slot_firstpfn ) < < PAGE_SHIFT ) ) ;
2005-04-16 15:20:36 -07:00
}
/*
2013-01-22 12:59:30 +01:00
* A node with nothing . We use it to avoid any special casing in
2009-09-24 09:34:26 -06:00
* cpumask_of_node
2005-04-16 15:20:36 -07:00
*/
static struct node_data null_node = {
. hub = {
. h_cpus = CPU_MASK_NONE
}
} ;
/*
* Currently , the intranode memory hole support assumes that each slot
* contains at least 32 MBytes of memory . We assume all bootmem data
* fits on the first slot .
*/
void __init prom_meminit ( void )
{
2019-10-03 14:27:24 +02:00
nasid_t node ;
2005-04-16 15:20:36 -07:00
mlreset ( ) ;
szmem ( ) ;
2018-11-12 22:18:01 +00:00
max_low_pfn = PHYS_PFN ( memblock_end_of_DRAM ( ) ) ;
2005-04-16 15:20:36 -07:00
2019-10-30 11:51:44 +01:00
for ( node = 0 ; node < MAX_NUMNODES ; node + + ) {
2005-04-16 15:20:36 -07:00
if ( node_online ( node ) ) {
node_mem_init ( node ) ;
continue ;
}
__node_data [ node ] = & null_node ;
}
}
2013-04-29 15:06:43 -07:00
extern void setup_zero_pages ( void ) ;
2005-04-16 15:20:36 -07:00
void __init paging_init ( void )
{
2006-09-25 23:31:10 -07:00
unsigned long zones_size [ MAX_NR_ZONES ] = { 0 , } ;
2005-04-16 15:20:36 -07:00
pagetable_init ( ) ;
2008-04-08 23:43:46 +02:00
zones_size [ ZONE_NORMAL ] = max_low_pfn ;
2020-06-03 15:57:10 -07:00
free_area_init ( zones_size ) ;
2005-04-16 15:20:36 -07:00
}
void __init mem_init ( void )
{
2013-07-03 15:04:04 -07:00
high_memory = ( void * ) __va ( get_num_physpages ( ) < < PAGE_SHIFT ) ;
2018-10-30 15:09:30 -07:00
memblock_free_all ( ) ;
2013-04-29 15:06:43 -07:00
setup_zero_pages ( ) ; /* This comes from node 0 */
2013-07-03 15:04:04 -07:00
mem_init_print_info ( NULL ) ;
2005-04-16 15:20:36 -07:00
}