| /* | 
 |  * Based on linux/arch/arm/pmsa-v7.c | 
 |  * | 
 |  * ARM PMSAv8 supporting functions. | 
 |  */ | 
 |  | 
 | #include <linux/memblock.h> | 
 | #include <linux/range.h> | 
 |  | 
 | #include <asm/cp15.h> | 
 | #include <asm/cputype.h> | 
 | #include <asm/mpu.h> | 
 |  | 
 | #include <asm/memory.h> | 
 | #include <asm/sections.h> | 
 |  | 
 | #include "mm.h" | 
 |  | 
 | #ifndef CONFIG_CPU_V7M | 
 |  | 
 | #define PRSEL	__ACCESS_CP15(c6, 0, c2, 1) | 
 | #define PRBAR	__ACCESS_CP15(c6, 0, c3, 0) | 
 | #define PRLAR	__ACCESS_CP15(c6, 0, c3, 1) | 
 |  | 
 | static inline u32 prlar_read(void) | 
 | { | 
 | 	return read_sysreg(PRLAR); | 
 | } | 
 |  | 
 | static inline u32 prbar_read(void) | 
 | { | 
 | 	return read_sysreg(PRBAR); | 
 | } | 
 |  | 
 | static inline void prsel_write(u32 v) | 
 | { | 
 | 	write_sysreg(v, PRSEL); | 
 | } | 
 |  | 
 | static inline void prbar_write(u32 v) | 
 | { | 
 | 	write_sysreg(v, PRBAR); | 
 | } | 
 |  | 
 | static inline void prlar_write(u32 v) | 
 | { | 
 | 	write_sysreg(v, PRLAR); | 
 | } | 
 | #else | 
 |  | 
 | static inline u32 prlar_read(void) | 
 | { | 
 | 	return readl_relaxed(BASEADDR_V7M_SCB + PMSAv8_RLAR); | 
 | } | 
 |  | 
 | static inline u32 prbar_read(void) | 
 | { | 
 | 	return readl_relaxed(BASEADDR_V7M_SCB + PMSAv8_RBAR); | 
 | } | 
 |  | 
 | static inline void prsel_write(u32 v) | 
 | { | 
 | 	writel_relaxed(v, BASEADDR_V7M_SCB + PMSAv8_RNR); | 
 | } | 
 |  | 
 | static inline void prbar_write(u32 v) | 
 | { | 
 | 	writel_relaxed(v, BASEADDR_V7M_SCB + PMSAv8_RBAR); | 
 | } | 
 |  | 
 | static inline void prlar_write(u32 v) | 
 | { | 
 | 	writel_relaxed(v, BASEADDR_V7M_SCB + PMSAv8_RLAR); | 
 | } | 
 |  | 
 | #endif | 
 |  | 
 | static struct range __initdata io[MPU_MAX_REGIONS]; | 
 | static struct range __initdata mem[MPU_MAX_REGIONS]; | 
 |  | 
 | static unsigned int __initdata mpu_max_regions; | 
 |  | 
 | static __init bool is_region_fixed(int number) | 
 | { | 
 | 	switch (number) { | 
 | 	case PMSAv8_XIP_REGION: | 
 | 	case PMSAv8_KERNEL_REGION: | 
 | 		return true; | 
 | 	default: | 
 | 		return false; | 
 | 	} | 
 | } | 
 |  | 
 | void __init pmsav8_adjust_lowmem_bounds(void) | 
 | { | 
 | 	phys_addr_t mem_end; | 
 | 	struct memblock_region *reg; | 
 | 	bool first = true; | 
 |  | 
 | 	for_each_memblock(memory, reg) { | 
 | 		if (first) { | 
 | 			phys_addr_t phys_offset = PHYS_OFFSET; | 
 |  | 
 | 			/* | 
 | 			 * Initially only use memory continuous from | 
 | 			 * PHYS_OFFSET */ | 
 | 			if (reg->base != phys_offset) | 
 | 				panic("First memory bank must be contiguous from PHYS_OFFSET"); | 
 | 			mem_end = reg->base + reg->size; | 
 | 			first = false; | 
 | 		} else { | 
 | 			/* | 
 | 			 * memblock auto merges contiguous blocks, remove | 
 | 			 * all blocks afterwards in one go (we can't remove | 
 | 			 * blocks separately while iterating) | 
 | 			 */ | 
 | 			pr_notice("Ignoring RAM after %pa, memory at %pa ignored\n", | 
 | 				  &mem_end, ®->base); | 
 | 			memblock_remove(reg->base, 0 - reg->base); | 
 | 			break; | 
 | 		} | 
 | 	} | 
 | } | 
 |  | 
 | static int __init __mpu_max_regions(void) | 
 | { | 
 | 	static int max_regions; | 
 | 	u32 mpuir; | 
 |  | 
 | 	if (max_regions) | 
 | 		return max_regions; | 
 |  | 
 | 	mpuir = read_cpuid_mputype(); | 
 |  | 
 | 	max_regions  = (mpuir & MPUIR_DREGION_SZMASK) >> MPUIR_DREGION; | 
 |  | 
 | 	return max_regions; | 
 | } | 
 |  | 
 | static int __init __pmsav8_setup_region(unsigned int number, u32 bar, u32 lar) | 
 | { | 
 | 	if (number > mpu_max_regions | 
 | 	    || number >= MPU_MAX_REGIONS) | 
 | 		return -ENOENT; | 
 |  | 
 | 	dsb(); | 
 | 	prsel_write(number); | 
 | 	isb(); | 
 | 	prbar_write(bar); | 
 | 	prlar_write(lar); | 
 |  | 
 | 	mpu_rgn_info.rgns[number].prbar = bar; | 
 | 	mpu_rgn_info.rgns[number].prlar = lar; | 
 |  | 
 | 	mpu_rgn_info.used++; | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int __init pmsav8_setup_ram(unsigned int number, phys_addr_t start,phys_addr_t end) | 
 | { | 
 | 	u32 bar, lar; | 
 |  | 
 | 	if (is_region_fixed(number)) | 
 | 		return -EINVAL; | 
 |  | 
 | 	bar = start; | 
 | 	lar = (end - 1) & ~(PMSAv8_MINALIGN - 1);; | 
 |  | 
 | 	bar |= PMSAv8_AP_PL1RW_PL0RW | PMSAv8_RGN_SHARED; | 
 | 	lar |= PMSAv8_LAR_IDX(PMSAv8_RGN_NORMAL) | PMSAv8_LAR_EN; | 
 |  | 
 | 	return __pmsav8_setup_region(number, bar, lar); | 
 | } | 
 |  | 
 | static int __init pmsav8_setup_io(unsigned int number, phys_addr_t start,phys_addr_t end) | 
 | { | 
 | 	u32 bar, lar; | 
 |  | 
 | 	if (is_region_fixed(number)) | 
 | 		return -EINVAL; | 
 |  | 
 | 	bar = start; | 
 | 	lar = (end - 1) & ~(PMSAv8_MINALIGN - 1);; | 
 |  | 
 | 	bar |= PMSAv8_AP_PL1RW_PL0RW | PMSAv8_RGN_SHARED | PMSAv8_BAR_XN; | 
 | 	lar |= PMSAv8_LAR_IDX(PMSAv8_RGN_DEVICE_nGnRnE) | PMSAv8_LAR_EN; | 
 |  | 
 | 	return __pmsav8_setup_region(number, bar, lar); | 
 | } | 
 |  | 
 | static int __init pmsav8_setup_fixed(unsigned int number, phys_addr_t start,phys_addr_t end) | 
 | { | 
 | 	u32 bar, lar; | 
 |  | 
 | 	if (!is_region_fixed(number)) | 
 | 		return -EINVAL; | 
 |  | 
 | 	bar = start; | 
 | 	lar = (end - 1) & ~(PMSAv8_MINALIGN - 1); | 
 |  | 
 | 	bar |= PMSAv8_AP_PL1RW_PL0NA | PMSAv8_RGN_SHARED; | 
 | 	lar |= PMSAv8_LAR_IDX(PMSAv8_RGN_NORMAL) | PMSAv8_LAR_EN; | 
 |  | 
 | 	prsel_write(number); | 
 | 	isb(); | 
 |  | 
 | 	if (prbar_read() != bar || prlar_read() != lar) | 
 | 		return -EINVAL; | 
 |  | 
 | 	/* Reserved region was set up early, we just need a record for secondaries */ | 
 | 	mpu_rgn_info.rgns[number].prbar = bar; | 
 | 	mpu_rgn_info.rgns[number].prlar = lar; | 
 |  | 
 | 	mpu_rgn_info.used++; | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | #ifndef CONFIG_CPU_V7M | 
 | static int __init pmsav8_setup_vector(unsigned int number, phys_addr_t start,phys_addr_t end) | 
 | { | 
 | 	u32 bar, lar; | 
 |  | 
 | 	if (number == PMSAv8_KERNEL_REGION) | 
 | 		return -EINVAL; | 
 |  | 
 | 	bar = start; | 
 | 	lar = (end - 1) & ~(PMSAv8_MINALIGN - 1); | 
 |  | 
 | 	bar |= PMSAv8_AP_PL1RW_PL0NA | PMSAv8_RGN_SHARED; | 
 | 	lar |= PMSAv8_LAR_IDX(PMSAv8_RGN_NORMAL) | PMSAv8_LAR_EN; | 
 |  | 
 | 	return __pmsav8_setup_region(number, bar, lar); | 
 | } | 
 | #endif | 
 |  | 
 | void __init pmsav8_setup(void) | 
 | { | 
 | 	int i, err = 0; | 
 | 	int region = PMSAv8_KERNEL_REGION; | 
 |  | 
 | 	/* How many regions are supported ? */ | 
 | 	mpu_max_regions = __mpu_max_regions(); | 
 |  | 
 | 	/* RAM: single chunk of memory */ | 
 | 	add_range(mem,  ARRAY_SIZE(mem), 0,  memblock.memory.regions[0].base, | 
 | 		  memblock.memory.regions[0].base + memblock.memory.regions[0].size); | 
 |  | 
 | 	/* IO: cover full 4G range */ | 
 | 	add_range(io, ARRAY_SIZE(io), 0, 0, 0xffffffff); | 
 |  | 
 | 	/* RAM and IO: exclude kernel */ | 
 | 	subtract_range(mem, ARRAY_SIZE(mem), __pa(KERNEL_START), __pa(KERNEL_END)); | 
 | 	subtract_range(io, ARRAY_SIZE(io),  __pa(KERNEL_START), __pa(KERNEL_END)); | 
 |  | 
 | #ifdef CONFIG_XIP_KERNEL | 
 | 	/* RAM and IO: exclude xip */ | 
 | 	subtract_range(mem, ARRAY_SIZE(mem), CONFIG_XIP_PHYS_ADDR, __pa(_exiprom)); | 
 | 	subtract_range(io, ARRAY_SIZE(io), CONFIG_XIP_PHYS_ADDR, __pa(_exiprom)); | 
 | #endif | 
 |  | 
 | #ifndef CONFIG_CPU_V7M | 
 | 	/* RAM and IO: exclude vectors */ | 
 | 	subtract_range(mem, ARRAY_SIZE(mem),  vectors_base, vectors_base + 2 * PAGE_SIZE); | 
 | 	subtract_range(io, ARRAY_SIZE(io),  vectors_base, vectors_base + 2 * PAGE_SIZE); | 
 | #endif | 
 | 	/* IO: exclude RAM */ | 
 | 	for (i = 0; i < ARRAY_SIZE(mem); i++) | 
 | 		subtract_range(io, ARRAY_SIZE(io), mem[i].start, mem[i].end); | 
 |  | 
 | 	/* Now program MPU */ | 
 |  | 
 | #ifdef CONFIG_XIP_KERNEL | 
 | 	/* ROM */ | 
 | 	err |= pmsav8_setup_fixed(PMSAv8_XIP_REGION, CONFIG_XIP_PHYS_ADDR, __pa(_exiprom)); | 
 | #endif | 
 | 	/* Kernel */ | 
 | 	err |= pmsav8_setup_fixed(region++, __pa(KERNEL_START), __pa(KERNEL_END)); | 
 |  | 
 |  | 
 | 	/* IO */ | 
 | 	for (i = 0; i < ARRAY_SIZE(io); i++) { | 
 | 		if (!io[i].end) | 
 | 			continue; | 
 |  | 
 | 		err |= pmsav8_setup_io(region++, io[i].start, io[i].end); | 
 | 	} | 
 |  | 
 | 	/* RAM */ | 
 | 	for (i = 0; i < ARRAY_SIZE(mem); i++) { | 
 | 		if (!mem[i].end) | 
 | 			continue; | 
 |  | 
 | 		err |= pmsav8_setup_ram(region++, mem[i].start, mem[i].end); | 
 | 	} | 
 |  | 
 | 	/* Vectors */ | 
 | #ifndef CONFIG_CPU_V7M | 
 | 	err |= pmsav8_setup_vector(region++, vectors_base, vectors_base + 2 * PAGE_SIZE); | 
 | #endif | 
 | 	if (err) | 
 | 		pr_warn("MPU region initialization failure! %d", err); | 
 | 	else | 
 | 		pr_info("Using ARM PMSAv8 Compliant MPU. Used %d of %d regions\n", | 
 | 			mpu_rgn_info.used, mpu_max_regions); | 
 | } |