2018-04-03 10:39:23 +01:00
/*
* 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 , & reg - > 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 ;
2019-01-25 15:26:32 +01:00
lar = ( end - 1 ) & ~ ( PMSAv8_MINALIGN - 1 ) ;
2018-04-03 10:39:23 +01:00
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 ;
2019-01-25 15:26:32 +01:00
lar = ( end - 1 ) & ~ ( PMSAv8_MINALIGN - 1 ) ;
2018-04-03 10:39:23 +01:00
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 ) ;
}