2013-01-18 13:42:20 +04:00
/*
* Copyright ( C ) 2004 , 2007 - 2010 , 2011 - 2012 Synopsys , Inc . ( www . synopsys . com )
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*/
# include <linux/kernel.h>
# include <linux/mm.h>
# include <linux/bootmem.h>
# include <linux/memblock.h>
2012-09-09 12:37:40 +04:00
# ifdef CONFIG_BLK_DEV_INITRD
# include <linux/initrd.h>
# endif
2016-04-26 19:29:34 +03:00
# include <linux/of_fdt.h>
2013-01-18 13:42:20 +04:00
# include <linux/swap.h>
# include <linux/module.h>
2015-10-28 16:36:10 +03:00
# include <linux/highmem.h>
2013-01-18 13:42:20 +04:00
# include <asm/page.h>
# include <asm/pgalloc.h>
# include <asm/sections.h>
# include <asm/arcregs.h>
pgd_t swapper_pg_dir [ PTRS_PER_PGD ] __aligned ( PAGE_SIZE ) ;
char empty_zero_page [ PAGE_SIZE ] __aligned ( PAGE_SIZE ) ;
EXPORT_SYMBOL ( empty_zero_page ) ;
2015-10-28 16:18:17 +03:00
static const unsigned long low_mem_start = CONFIG_LINUX_LINK_BASE ;
static unsigned long low_mem_sz ;
2013-01-18 13:42:20 +04:00
2015-10-28 16:36:10 +03:00
# ifdef CONFIG_HIGHMEM
2016-04-18 08:19:56 +03:00
static unsigned long min_high_pfn , max_high_pfn ;
2015-10-28 16:36:10 +03:00
static u64 high_mem_start ;
static u64 high_mem_sz ;
# endif
2016-04-18 08:19:56 +03:00
# ifdef CONFIG_DISCONTIGMEM
struct pglist_data node_data [ MAX_NUMNODES ] __read_mostly ;
EXPORT_SYMBOL ( node_data ) ;
# endif
2017-01-18 23:59:21 +03:00
long __init arc_get_mem_sz ( void )
{
return low_mem_sz ;
}
2013-01-18 13:42:20 +04:00
/* User can over-ride above with "mem=nnn[KkMm]" in cmdline */
static int __init setup_mem_sz ( char * str )
{
2015-10-28 16:18:17 +03:00
low_mem_sz = memparse ( str , NULL ) & PAGE_MASK ;
2013-01-18 13:42:20 +04:00
/* early console might not be setup yet - it will show up later */
2015-10-28 16:18:17 +03:00
pr_info ( " \" mem=%s \" : mem sz set to %ldM \n " , str , TO_MB ( low_mem_sz ) ) ;
2013-01-18 13:42:20 +04:00
return 0 ;
}
early_param ( " mem " , setup_mem_sz ) ;
2013-01-22 15:30:52 +04:00
void __init early_init_dt_add_memory_arch ( u64 base , u64 size )
{
2015-10-28 16:36:10 +03:00
int in_use = 0 ;
if ( ! low_mem_sz ) {
2015-12-15 11:27:16 +03:00
if ( base ! = low_mem_start )
panic ( " CONFIG_LINUX_LINK_BASE != DT memory { } " ) ;
2015-10-28 16:36:10 +03:00
low_mem_sz = size ;
in_use = 1 ;
} else {
# ifdef CONFIG_HIGHMEM
high_mem_start = base ;
high_mem_sz = size ;
in_use = 1 ;
# endif
}
2015-01-23 15:40:26 +03:00
2015-10-28 16:36:10 +03:00
pr_info ( " Memory @ %llx [%lldM] %s \n " ,
base , TO_MB ( size ) , ! in_use ? " Not used " : " " ) ;
2013-01-22 15:30:52 +04:00
}
2012-09-09 12:37:40 +04:00
# ifdef CONFIG_BLK_DEV_INITRD
static int __init early_initrd ( char * p )
{
unsigned long start , size ;
char * endp ;
start = memparse ( p , & endp ) ;
if ( * endp = = ' , ' ) {
size = memparse ( endp + 1 , NULL ) ;
initrd_start = ( unsigned long ) __va ( start ) ;
initrd_end = ( unsigned long ) __va ( start + size ) ;
}
return 0 ;
}
early_param ( " initrd " , early_initrd ) ;
# endif
2013-01-18 13:42:20 +04:00
/*
* First memory setup routine called from setup_arch ( )
* 1. setup swapper ' s mm @ init_mm
* 2. Count the pages we have and setup bootmem allocator
* 3. zone setup
*/
void __init setup_arch_memory ( void )
{
2015-02-13 11:29:53 +03:00
unsigned long zones_size [ MAX_NR_ZONES ] ;
2015-10-28 16:36:10 +03:00
unsigned long zones_holes [ MAX_NR_ZONES ] ;
2013-01-18 13:42:20 +04:00
init_mm . start_code = ( unsigned long ) _text ;
init_mm . end_code = ( unsigned long ) _etext ;
init_mm . end_data = ( unsigned long ) _edata ;
init_mm . brk = ( unsigned long ) _end ;
/* first page of system - kernel .vector starts here */
2015-02-13 11:29:53 +03:00
min_low_pfn = ARCH_PFN_OFFSET ;
2013-01-18 13:42:20 +04:00
2015-10-28 16:18:17 +03:00
/* Last usable page of low mem */
max_low_pfn = max_pfn = PFN_DOWN ( low_mem_start + low_mem_sz ) ;
2013-01-18 13:42:20 +04:00
2016-04-18 08:19:56 +03:00
# ifdef CONFIG_FLATMEM
/* pfn_valid() uses this */
max_mapnr = max_low_pfn - min_low_pfn ;
2015-10-28 16:36:10 +03:00
# endif
2015-10-28 16:18:17 +03:00
/*------------- bootmem allocator setup -----------------------*/
2015-10-28 16:36:10 +03:00
/*
* seed the bootmem allocator after any DT memory node parsing or
* " mem=xxx " cmdline overrides have potentially updated @ arc_mem_sz
*
* Only low mem is added , otherwise we have crashes when allocating
* mem_map [ ] itself . NO_BOOTMEM allocates mem_map [ ] at the end of
* avail memory , ending in highmem with a > 32 - bit address . However
* it then tries to memset it with a truncaed 32 - bit handle , causing
* the crash
*/
2016-04-18 08:19:56 +03:00
memblock_add_node ( low_mem_start , low_mem_sz , 0 ) ;
2015-10-28 16:18:17 +03:00
memblock_reserve ( low_mem_start , __pa ( _end ) - low_mem_start ) ;
2013-01-18 13:42:20 +04:00
2012-09-09 12:37:40 +04:00
# ifdef CONFIG_BLK_DEV_INITRD
if ( initrd_start )
memblock_reserve ( __pa ( initrd_start ) , initrd_end - initrd_start ) ;
# endif
2016-04-26 19:29:34 +03:00
early_init_fdt_reserve_self ( ) ;
early_init_fdt_scan_reserved_mem ( ) ;
2013-01-18 13:42:20 +04:00
memblock_dump_all ( ) ;
2015-10-28 16:18:17 +03:00
/*----------------- node/zones setup --------------------------*/
2013-01-18 13:42:20 +04:00
memset ( zones_size , 0 , sizeof ( zones_size ) ) ;
2015-10-28 16:36:10 +03:00
memset ( zones_holes , 0 , sizeof ( zones_holes ) ) ;
2015-10-28 16:18:17 +03:00
zones_size [ ZONE_NORMAL ] = max_low_pfn - min_low_pfn ;
2015-10-28 16:36:10 +03:00
zones_holes [ ZONE_NORMAL ] = 0 ;
2013-01-18 13:42:20 +04:00
/*
* We can ' t use the helper free_area_init ( zones [ ] ) because it uses
* PAGE_OFFSET to compute the @ min_low_pfn which would be wrong
* when our kernel doesn ' t start at PAGE_OFFSET , i . e .
* PAGE_OFFSET ! = CONFIG_LINUX_LINK_BASE
*/
free_area_init_node ( 0 , /* node-id */
zones_size , /* num pages per zone */
min_low_pfn , /* first pfn of node */
2015-10-28 16:36:10 +03:00
zones_holes ) ; /* holes */
2015-02-13 11:29:53 +03:00
2015-10-28 16:36:10 +03:00
# ifdef CONFIG_HIGHMEM
2016-04-18 08:19:56 +03:00
/*
* Populate a new node with highmem
*
* On ARC ( w / o PAE ) HIGHMEM addresses are actually smaller ( 0 based )
* than addresses in normal ala low memory ( 0x8000 _0000 based ) .
* Even with PAE , the huge peripheral space hole would waste a lot of
* mem with single mem_map [ ] . This warrants a mem_map per region design .
* Thus HIGHMEM on ARC is imlemented with DISCONTIGMEM .
*
* DISCONTIGMEM in turns requires multiple nodes . node 0 above is
* populated with normal memory zone while node 1 only has highmem
*/
node_set_online ( 1 ) ;
min_high_pfn = PFN_DOWN ( high_mem_start ) ;
max_high_pfn = PFN_DOWN ( high_mem_start + high_mem_sz ) ;
zones_size [ ZONE_NORMAL ] = 0 ;
zones_holes [ ZONE_NORMAL ] = 0 ;
zones_size [ ZONE_HIGHMEM ] = max_high_pfn - min_high_pfn ;
zones_holes [ ZONE_HIGHMEM ] = 0 ;
free_area_init_node ( 1 , /* node-id */
zones_size , /* num pages per zone */
min_high_pfn , /* first pfn of node */
zones_holes ) ; /* holes */
2015-10-28 16:36:10 +03:00
high_memory = ( void * ) ( min_high_pfn < < PAGE_SHIFT ) ;
kmap_init ( ) ;
# endif
2013-01-18 13:42:20 +04:00
}
/*
* mem_init - initializes memory
*
* Frees up bootmem
* Calculates and displays memory available / used
*/
void __init mem_init ( void )
{
2015-10-28 16:36:10 +03:00
# ifdef CONFIG_HIGHMEM
unsigned long tmp ;
reset_all_zones_managed_pages ( ) ;
2016-04-18 08:19:56 +03:00
for ( tmp = min_high_pfn ; tmp < max_high_pfn ; tmp + + )
2015-10-28 16:36:10 +03:00
free_highmem_page ( pfn_to_page ( tmp ) ) ;
# endif
2013-07-04 02:03:24 +04:00
free_all_bootmem ( ) ;
2013-07-04 02:03:47 +04:00
mem_init_print_info ( NULL ) ;
2013-01-18 13:42:20 +04:00
}
/*
* free_initmem : Free all the __init memory .
*/
2016-08-03 00:03:33 +03:00
void __ref free_initmem ( void )
2013-01-18 13:42:20 +04:00
{
2013-07-04 02:02:51 +04:00
free_initmem_default ( - 1 ) ;
2013-01-18 13:42:20 +04:00
}
# ifdef CONFIG_BLK_DEV_INITRD
void __init free_initrd_mem ( unsigned long start , unsigned long end )
{
2013-07-04 02:02:51 +04:00
free_reserved_area ( ( void * ) start , ( void * ) end , - 1 , " initrd " ) ;
2013-01-18 13:42:20 +04:00
}
# endif