162306a36Sopenharmony_ci/*
262306a36Sopenharmony_ci * Copyright (C) 2007-2008 Michal Simek <monstr@monstr.eu>
362306a36Sopenharmony_ci * Copyright (C) 2006 Atmark Techno, Inc.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
662306a36Sopenharmony_ci * License. See the file "COPYING" in the main directory of this archive
762306a36Sopenharmony_ci * for more details.
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/dma-map-ops.h>
1162306a36Sopenharmony_ci#include <linux/memblock.h>
1262306a36Sopenharmony_ci#include <linux/init.h>
1362306a36Sopenharmony_ci#include <linux/kernel.h>
1462306a36Sopenharmony_ci#include <linux/mm.h> /* mem_init */
1562306a36Sopenharmony_ci#include <linux/initrd.h>
1662306a36Sopenharmony_ci#include <linux/of_fdt.h>
1762306a36Sopenharmony_ci#include <linux/pagemap.h>
1862306a36Sopenharmony_ci#include <linux/pfn.h>
1962306a36Sopenharmony_ci#include <linux/slab.h>
2062306a36Sopenharmony_ci#include <linux/swap.h>
2162306a36Sopenharmony_ci#include <linux/export.h>
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_ci#include <asm/page.h>
2462306a36Sopenharmony_ci#include <asm/mmu_context.h>
2562306a36Sopenharmony_ci#include <asm/pgalloc.h>
2662306a36Sopenharmony_ci#include <asm/sections.h>
2762306a36Sopenharmony_ci#include <asm/tlb.h>
2862306a36Sopenharmony_ci#include <asm/fixmap.h>
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci/* Use for MMU and noMMU because of PCI generic code */
3162306a36Sopenharmony_ciint mem_init_done;
3262306a36Sopenharmony_ci
3362306a36Sopenharmony_cichar *klimit = _end;
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci/*
3662306a36Sopenharmony_ci * Initialize the bootmem system and give it all the memory we
3762306a36Sopenharmony_ci * have available.
3862306a36Sopenharmony_ci */
3962306a36Sopenharmony_ciunsigned long memory_start;
4062306a36Sopenharmony_ciEXPORT_SYMBOL(memory_start);
4162306a36Sopenharmony_ciunsigned long memory_size;
4262306a36Sopenharmony_ciEXPORT_SYMBOL(memory_size);
4362306a36Sopenharmony_ciunsigned long lowmem_size;
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_ciEXPORT_SYMBOL(min_low_pfn);
4662306a36Sopenharmony_ciEXPORT_SYMBOL(max_low_pfn);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci#ifdef CONFIG_HIGHMEM
4962306a36Sopenharmony_cistatic void __init highmem_init(void)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	pr_debug("%x\n", (u32)PKMAP_BASE);
5262306a36Sopenharmony_ci	map_page(PKMAP_BASE, 0, 0);	/* XXX gross */
5362306a36Sopenharmony_ci	pkmap_page_table = virt_to_kpte(PKMAP_BASE);
5462306a36Sopenharmony_ci}
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_cistatic void __meminit highmem_setup(void)
5762306a36Sopenharmony_ci{
5862306a36Sopenharmony_ci	unsigned long pfn;
5962306a36Sopenharmony_ci
6062306a36Sopenharmony_ci	for (pfn = max_low_pfn; pfn < max_pfn; ++pfn) {
6162306a36Sopenharmony_ci		struct page *page = pfn_to_page(pfn);
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci		/* FIXME not sure about */
6462306a36Sopenharmony_ci		if (!memblock_is_reserved(pfn << PAGE_SHIFT))
6562306a36Sopenharmony_ci			free_highmem_page(page);
6662306a36Sopenharmony_ci	}
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci#endif /* CONFIG_HIGHMEM */
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci/*
7162306a36Sopenharmony_ci * paging_init() sets up the page tables - in fact we've already done this.
7262306a36Sopenharmony_ci */
7362306a36Sopenharmony_cistatic void __init paging_init(void)
7462306a36Sopenharmony_ci{
7562306a36Sopenharmony_ci	unsigned long zones_size[MAX_NR_ZONES];
7662306a36Sopenharmony_ci	int idx;
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	/* Setup fixmaps */
7962306a36Sopenharmony_ci	for (idx = 0; idx < __end_of_fixed_addresses; idx++)
8062306a36Sopenharmony_ci		clear_fixmap(idx);
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	/* Clean every zones */
8362306a36Sopenharmony_ci	memset(zones_size, 0, sizeof(zones_size));
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci#ifdef CONFIG_HIGHMEM
8662306a36Sopenharmony_ci	highmem_init();
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_ci	zones_size[ZONE_DMA] = max_low_pfn;
8962306a36Sopenharmony_ci	zones_size[ZONE_HIGHMEM] = max_pfn;
9062306a36Sopenharmony_ci#else
9162306a36Sopenharmony_ci	zones_size[ZONE_DMA] = max_pfn;
9262306a36Sopenharmony_ci#endif
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci	/* We don't have holes in memory map */
9562306a36Sopenharmony_ci	free_area_init(zones_size);
9662306a36Sopenharmony_ci}
9762306a36Sopenharmony_ci
9862306a36Sopenharmony_civoid __init setup_memory(void)
9962306a36Sopenharmony_ci{
10062306a36Sopenharmony_ci	/*
10162306a36Sopenharmony_ci	 * Kernel:
10262306a36Sopenharmony_ci	 * start: base phys address of kernel - page align
10362306a36Sopenharmony_ci	 * end: base phys address of kernel - page align
10462306a36Sopenharmony_ci	 *
10562306a36Sopenharmony_ci	 * min_low_pfn - the first page (mm/bootmem.c - node_boot_start)
10662306a36Sopenharmony_ci	 * max_low_pfn
10762306a36Sopenharmony_ci	 * max_mapnr - the first unused page (mm/bootmem.c - node_low_pfn)
10862306a36Sopenharmony_ci	 */
10962306a36Sopenharmony_ci
11062306a36Sopenharmony_ci	/* memory start is from the kernel end (aligned) to higher addr */
11162306a36Sopenharmony_ci	min_low_pfn = memory_start >> PAGE_SHIFT; /* minimum for allocation */
11262306a36Sopenharmony_ci	/* RAM is assumed contiguous */
11362306a36Sopenharmony_ci	max_mapnr = memory_size >> PAGE_SHIFT;
11462306a36Sopenharmony_ci	max_low_pfn = ((u64)memory_start + (u64)lowmem_size) >> PAGE_SHIFT;
11562306a36Sopenharmony_ci	max_pfn = ((u64)memory_start + (u64)memory_size) >> PAGE_SHIFT;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	pr_info("%s: max_mapnr: %#lx\n", __func__, max_mapnr);
11862306a36Sopenharmony_ci	pr_info("%s: min_low_pfn: %#lx\n", __func__, min_low_pfn);
11962306a36Sopenharmony_ci	pr_info("%s: max_low_pfn: %#lx\n", __func__, max_low_pfn);
12062306a36Sopenharmony_ci	pr_info("%s: max_pfn: %#lx\n", __func__, max_pfn);
12162306a36Sopenharmony_ci
12262306a36Sopenharmony_ci	paging_init();
12362306a36Sopenharmony_ci}
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_civoid __init mem_init(void)
12662306a36Sopenharmony_ci{
12762306a36Sopenharmony_ci	high_memory = (void *)__va(memory_start + lowmem_size - 1);
12862306a36Sopenharmony_ci
12962306a36Sopenharmony_ci	/* this will put all memory onto the freelists */
13062306a36Sopenharmony_ci	memblock_free_all();
13162306a36Sopenharmony_ci#ifdef CONFIG_HIGHMEM
13262306a36Sopenharmony_ci	highmem_setup();
13362306a36Sopenharmony_ci#endif
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	mem_init_done = 1;
13662306a36Sopenharmony_ci}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ciint page_is_ram(unsigned long pfn)
13962306a36Sopenharmony_ci{
14062306a36Sopenharmony_ci	return pfn < max_low_pfn;
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci/*
14462306a36Sopenharmony_ci * Check for command-line options that affect what MMU_init will do.
14562306a36Sopenharmony_ci */
14662306a36Sopenharmony_cistatic void mm_cmdline_setup(void)
14762306a36Sopenharmony_ci{
14862306a36Sopenharmony_ci	unsigned long maxmem = 0;
14962306a36Sopenharmony_ci	char *p = cmd_line;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	/* Look for mem= option on command line */
15262306a36Sopenharmony_ci	p = strstr(cmd_line, "mem=");
15362306a36Sopenharmony_ci	if (p) {
15462306a36Sopenharmony_ci		p += 4;
15562306a36Sopenharmony_ci		maxmem = memparse(p, &p);
15662306a36Sopenharmony_ci		if (maxmem && memory_size > maxmem) {
15762306a36Sopenharmony_ci			memory_size = maxmem;
15862306a36Sopenharmony_ci			memblock.memory.regions[0].size = memory_size;
15962306a36Sopenharmony_ci		}
16062306a36Sopenharmony_ci	}
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_ci/*
16462306a36Sopenharmony_ci * MMU_init_hw does the chip-specific initialization of the MMU hardware.
16562306a36Sopenharmony_ci */
16662306a36Sopenharmony_cistatic void __init mmu_init_hw(void)
16762306a36Sopenharmony_ci{
16862306a36Sopenharmony_ci	/*
16962306a36Sopenharmony_ci	 * The Zone Protection Register (ZPR) defines how protection will
17062306a36Sopenharmony_ci	 * be applied to every page which is a member of a given zone. At
17162306a36Sopenharmony_ci	 * present, we utilize only two of the zones.
17262306a36Sopenharmony_ci	 * The zone index bits (of ZSEL) in the PTE are used for software
17362306a36Sopenharmony_ci	 * indicators, except the LSB.  For user access, zone 1 is used,
17462306a36Sopenharmony_ci	 * for kernel access, zone 0 is used.  We set all but zone 1
17562306a36Sopenharmony_ci	 * to zero, allowing only kernel access as indicated in the PTE.
17662306a36Sopenharmony_ci	 * For zone 1, we set a 01 binary (a value of 10 will not work)
17762306a36Sopenharmony_ci	 * to allow user access as indicated in the PTE.  This also allows
17862306a36Sopenharmony_ci	 * kernel access as indicated in the PTE.
17962306a36Sopenharmony_ci	 */
18062306a36Sopenharmony_ci	__asm__ __volatile__ ("ori r11, r0, 0x10000000;" \
18162306a36Sopenharmony_ci			"mts rzpr, r11;"
18262306a36Sopenharmony_ci			: : : "r11");
18362306a36Sopenharmony_ci}
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_ci/*
18662306a36Sopenharmony_ci * MMU_init sets up the basic memory mappings for the kernel,
18762306a36Sopenharmony_ci * including both RAM and possibly some I/O regions,
18862306a36Sopenharmony_ci * and sets up the page tables and the MMU hardware ready to go.
18962306a36Sopenharmony_ci */
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci/* called from head.S */
19262306a36Sopenharmony_ciasmlinkage void __init mmu_init(void)
19362306a36Sopenharmony_ci{
19462306a36Sopenharmony_ci	unsigned int kstart, ksize;
19562306a36Sopenharmony_ci
19662306a36Sopenharmony_ci	if (!memblock.reserved.cnt) {
19762306a36Sopenharmony_ci		pr_emerg("Error memory count\n");
19862306a36Sopenharmony_ci		machine_restart(NULL);
19962306a36Sopenharmony_ci	}
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_ci	if ((u32) memblock.memory.regions[0].size < 0x400000) {
20262306a36Sopenharmony_ci		pr_emerg("Memory must be greater than 4MB\n");
20362306a36Sopenharmony_ci		machine_restart(NULL);
20462306a36Sopenharmony_ci	}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	if ((u32) memblock.memory.regions[0].size < kernel_tlb) {
20762306a36Sopenharmony_ci		pr_emerg("Kernel size is greater than memory node\n");
20862306a36Sopenharmony_ci		machine_restart(NULL);
20962306a36Sopenharmony_ci	}
21062306a36Sopenharmony_ci
21162306a36Sopenharmony_ci	/* Find main memory where the kernel is */
21262306a36Sopenharmony_ci	memory_start = (u32) memblock.memory.regions[0].base;
21362306a36Sopenharmony_ci	lowmem_size = memory_size = (u32) memblock.memory.regions[0].size;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	if (lowmem_size > CONFIG_LOWMEM_SIZE) {
21662306a36Sopenharmony_ci		lowmem_size = CONFIG_LOWMEM_SIZE;
21762306a36Sopenharmony_ci#ifndef CONFIG_HIGHMEM
21862306a36Sopenharmony_ci		memory_size = lowmem_size;
21962306a36Sopenharmony_ci#endif
22062306a36Sopenharmony_ci	}
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	mm_cmdline_setup(); /* FIXME parse args from command line - not used */
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	/*
22562306a36Sopenharmony_ci	 * Map out the kernel text/data/bss from the available physical
22662306a36Sopenharmony_ci	 * memory.
22762306a36Sopenharmony_ci	 */
22862306a36Sopenharmony_ci	kstart = __pa(CONFIG_KERNEL_START); /* kernel start */
22962306a36Sopenharmony_ci	/* kernel size */
23062306a36Sopenharmony_ci	ksize = PAGE_ALIGN(((u32)_end - (u32)CONFIG_KERNEL_START));
23162306a36Sopenharmony_ci	memblock_reserve(kstart, ksize);
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_ci#if defined(CONFIG_BLK_DEV_INITRD)
23462306a36Sopenharmony_ci	/* Remove the init RAM disk from the available memory. */
23562306a36Sopenharmony_ci	if (initrd_start) {
23662306a36Sopenharmony_ci		unsigned long size;
23762306a36Sopenharmony_ci		size = initrd_end - initrd_start;
23862306a36Sopenharmony_ci		memblock_reserve(__virt_to_phys(initrd_start), size);
23962306a36Sopenharmony_ci	}
24062306a36Sopenharmony_ci#endif /* CONFIG_BLK_DEV_INITRD */
24162306a36Sopenharmony_ci
24262306a36Sopenharmony_ci	/* Initialize the MMU hardware */
24362306a36Sopenharmony_ci	mmu_init_hw();
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	/* Map in all of RAM starting at CONFIG_KERNEL_START */
24662306a36Sopenharmony_ci	mapin_ram();
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	/* Extend vmalloc and ioremap area as big as possible */
24962306a36Sopenharmony_ci#ifdef CONFIG_HIGHMEM
25062306a36Sopenharmony_ci	ioremap_base = ioremap_bot = PKMAP_BASE;
25162306a36Sopenharmony_ci#else
25262306a36Sopenharmony_ci	ioremap_base = ioremap_bot = FIXADDR_START;
25362306a36Sopenharmony_ci#endif
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	/* Initialize the context management stuff */
25662306a36Sopenharmony_ci	mmu_context_init();
25762306a36Sopenharmony_ci
25862306a36Sopenharmony_ci	/* Shortly after that, the entire linear mapping will be available */
25962306a36Sopenharmony_ci	/* This will also cause that unflatten device tree will be allocated
26062306a36Sopenharmony_ci	 * inside 768MB limit */
26162306a36Sopenharmony_ci	memblock_set_current_limit(memory_start + lowmem_size - 1);
26262306a36Sopenharmony_ci
26362306a36Sopenharmony_ci	parse_early_param();
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	early_init_fdt_scan_reserved_mem();
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	/* CMA initialization */
26862306a36Sopenharmony_ci	dma_contiguous_reserve(memory_start + lowmem_size - 1);
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	memblock_dump_all();
27162306a36Sopenharmony_ci}
27262306a36Sopenharmony_ci
27362306a36Sopenharmony_cistatic const pgprot_t protection_map[16] = {
27462306a36Sopenharmony_ci	[VM_NONE]					= PAGE_NONE,
27562306a36Sopenharmony_ci	[VM_READ]					= PAGE_READONLY_X,
27662306a36Sopenharmony_ci	[VM_WRITE]					= PAGE_COPY,
27762306a36Sopenharmony_ci	[VM_WRITE | VM_READ]				= PAGE_COPY_X,
27862306a36Sopenharmony_ci	[VM_EXEC]					= PAGE_READONLY,
27962306a36Sopenharmony_ci	[VM_EXEC | VM_READ]				= PAGE_READONLY_X,
28062306a36Sopenharmony_ci	[VM_EXEC | VM_WRITE]				= PAGE_COPY,
28162306a36Sopenharmony_ci	[VM_EXEC | VM_WRITE | VM_READ]			= PAGE_COPY_X,
28262306a36Sopenharmony_ci	[VM_SHARED]					= PAGE_NONE,
28362306a36Sopenharmony_ci	[VM_SHARED | VM_READ]				= PAGE_READONLY_X,
28462306a36Sopenharmony_ci	[VM_SHARED | VM_WRITE]				= PAGE_SHARED,
28562306a36Sopenharmony_ci	[VM_SHARED | VM_WRITE | VM_READ]		= PAGE_SHARED_X,
28662306a36Sopenharmony_ci	[VM_SHARED | VM_EXEC]				= PAGE_READONLY,
28762306a36Sopenharmony_ci	[VM_SHARED | VM_EXEC | VM_READ]			= PAGE_READONLY_X,
28862306a36Sopenharmony_ci	[VM_SHARED | VM_EXEC | VM_WRITE]		= PAGE_SHARED,
28962306a36Sopenharmony_ci	[VM_SHARED | VM_EXEC | VM_WRITE | VM_READ]	= PAGE_SHARED_X
29062306a36Sopenharmony_ci};
29162306a36Sopenharmony_ciDECLARE_VM_GET_PAGE_PROT
292