diff options
Diffstat (limited to 'arch/arm/mm/pmsa-v8.c')
| -rw-r--r-- | arch/arm/mm/pmsa-v8.c | 307 | 
1 files changed, 307 insertions, 0 deletions
| diff --git a/arch/arm/mm/pmsa-v8.c b/arch/arm/mm/pmsa-v8.c new file mode 100644 index 000000000000..617a83def88a --- /dev/null +++ b/arch/arm/mm/pmsa-v8.c @@ -0,0 +1,307 @@ +/* + * 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); +} |