| /* | 
 |  * Copyright IBM Corp. 2008, 2009 | 
 |  * | 
 |  * Author(s): Heiko Carstens <heiko.carstens@de.ibm.com> | 
 |  */ | 
 |  | 
 | #include <linux/kernel.h> | 
 | #include <linux/module.h> | 
 | #include <asm/ipl.h> | 
 | #include <asm/sclp.h> | 
 | #include <asm/setup.h> | 
 |  | 
 | #define ADDR2G (1ULL << 31) | 
 |  | 
 | static void find_memory_chunks(struct mem_chunk chunk[]) | 
 | { | 
 | 	unsigned long long memsize, rnmax, rzm; | 
 | 	unsigned long addr = 0, size; | 
 | 	int i = 0, type; | 
 |  | 
 | 	rzm = sclp_get_rzm(); | 
 | 	rnmax = sclp_get_rnmax(); | 
 | 	memsize = rzm * rnmax; | 
 | 	if (!rzm) | 
 | 		rzm = 1ULL << 17; | 
 | 	if (sizeof(long) == 4) { | 
 | 		rzm = min(ADDR2G, rzm); | 
 | 		memsize = memsize ? min(ADDR2G, memsize) : ADDR2G; | 
 | 	} | 
 | 	do { | 
 | 		size = 0; | 
 | 		type = tprot(addr); | 
 | 		do { | 
 | 			size += rzm; | 
 | 			if (memsize && addr + size >= memsize) | 
 | 				break; | 
 | 		} while (type == tprot(addr + size)); | 
 | 		if (type == CHUNK_READ_WRITE || type == CHUNK_READ_ONLY) { | 
 | 			chunk[i].addr = addr; | 
 | 			chunk[i].size = size; | 
 | 			chunk[i].type = type; | 
 | 			i++; | 
 | 		} | 
 | 		addr += size; | 
 | 	} while (addr < memsize && i < MEMORY_CHUNKS); | 
 | } | 
 |  | 
 | void detect_memory_layout(struct mem_chunk chunk[]) | 
 | { | 
 | 	unsigned long flags, cr0; | 
 |  | 
 | 	memset(chunk, 0, MEMORY_CHUNKS * sizeof(struct mem_chunk)); | 
 | 	/* Disable IRQs, DAT and low address protection so tprot does the | 
 | 	 * right thing and we don't get scheduled away with low address | 
 | 	 * protection disabled. | 
 | 	 */ | 
 | 	flags = __arch_local_irq_stnsm(0xf8); | 
 | 	__ctl_store(cr0, 0, 0); | 
 | 	__ctl_clear_bit(0, 28); | 
 | 	find_memory_chunks(chunk); | 
 | 	__ctl_load(cr0, 0, 0); | 
 | 	arch_local_irq_restore(flags); | 
 | } | 
 | EXPORT_SYMBOL(detect_memory_layout); |