1/*
2 * Copyright IBM Corp. 2008, 2009
3 *
4 * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com>
5 */
6
7#include <linux/kernel.h>
8#include <linux/module.h>
9#include <linux/memblock.h>
10#include <linux/init.h>
11#include <linux/debugfs.h>
12#include <linux/seq_file.h>
13#include <asm/ipl.h>
14#include <asm/sclp.h>
15#include <asm/setup.h>
16
17#define ADDR2G (1ULL << 31)
18
19#define CHUNK_READ_WRITE 0
20#define CHUNK_READ_ONLY  1
21
22static inline void memblock_physmem_add(phys_addr_t start, phys_addr_t size)
23{
24	memblock_add_range(&memblock.memory, start, size, 0, 0);
25	memblock_add_range(&memblock.physmem, start, size, 0, 0);
26}
27
28void __init detect_memory_memblock(void)
29{
30	unsigned long long memsize, rnmax, rzm;
31	unsigned long addr, size;
32	int type;
33
34	rzm = sclp_get_rzm();
35	rnmax = sclp_get_rnmax();
36	memsize = rzm * rnmax;
37	if (!rzm)
38		rzm = 1ULL << 17;
39	if (IS_ENABLED(CONFIG_32BIT)) {
40		rzm = min(ADDR2G, rzm);
41		memsize = min(ADDR2G, memsize);
42	}
43	max_physmem_end = memsize;
44	addr = 0;
45	/* keep memblock lists close to the kernel */
46	memblock_set_bottom_up(true);
47	do {
48		size = 0;
49		type = tprot(addr);
50		do {
51			size += rzm;
52			if (max_physmem_end && addr + size >= max_physmem_end)
53				break;
54		} while (type == tprot(addr + size));
55		if (type == CHUNK_READ_WRITE || type == CHUNK_READ_ONLY) {
56			if (max_physmem_end && (addr + size > max_physmem_end))
57				size = max_physmem_end - addr;
58			memblock_physmem_add(addr, size);
59		}
60		addr += size;
61	} while (addr < max_physmem_end);
62	memblock_set_bottom_up(false);
63	if (!max_physmem_end)
64		max_physmem_end = memblock_end_of_DRAM();
65}
66