linux/arch/arm/mm/pmsa-v8.c
<<
>>
Prefs
   1/*
   2 * Based on linux/arch/arm/pmsa-v7.c
   3 *
   4 * ARM PMSAv8 supporting functions.
   5 */
   6
   7#include <linux/memblock.h>
   8#include <linux/range.h>
   9
  10#include <asm/cp15.h>
  11#include <asm/cputype.h>
  12#include <asm/mpu.h>
  13
  14#include <asm/memory.h>
  15#include <asm/sections.h>
  16
  17#include "mm.h"
  18
  19#ifndef CONFIG_CPU_V7M
  20
  21#define PRSEL   __ACCESS_CP15(c6, 0, c2, 1)
  22#define PRBAR   __ACCESS_CP15(c6, 0, c3, 0)
  23#define PRLAR   __ACCESS_CP15(c6, 0, c3, 1)
  24
  25static inline u32 prlar_read(void)
  26{
  27        return read_sysreg(PRLAR);
  28}
  29
  30static inline u32 prbar_read(void)
  31{
  32        return read_sysreg(PRBAR);
  33}
  34
  35static inline void prsel_write(u32 v)
  36{
  37        write_sysreg(v, PRSEL);
  38}
  39
  40static inline void prbar_write(u32 v)
  41{
  42        write_sysreg(v, PRBAR);
  43}
  44
  45static inline void prlar_write(u32 v)
  46{
  47        write_sysreg(v, PRLAR);
  48}
  49#else
  50
  51static inline u32 prlar_read(void)
  52{
  53        return readl_relaxed(BASEADDR_V7M_SCB + PMSAv8_RLAR);
  54}
  55
  56static inline u32 prbar_read(void)
  57{
  58        return readl_relaxed(BASEADDR_V7M_SCB + PMSAv8_RBAR);
  59}
  60
  61static inline void prsel_write(u32 v)
  62{
  63        writel_relaxed(v, BASEADDR_V7M_SCB + PMSAv8_RNR);
  64}
  65
  66static inline void prbar_write(u32 v)
  67{
  68        writel_relaxed(v, BASEADDR_V7M_SCB + PMSAv8_RBAR);
  69}
  70
  71static inline void prlar_write(u32 v)
  72{
  73        writel_relaxed(v, BASEADDR_V7M_SCB + PMSAv8_RLAR);
  74}
  75
  76#endif
  77
  78static struct range __initdata io[MPU_MAX_REGIONS];
  79static struct range __initdata mem[MPU_MAX_REGIONS];
  80
  81static unsigned int __initdata mpu_max_regions;
  82
  83static __init bool is_region_fixed(int number)
  84{
  85        switch (number) {
  86        case PMSAv8_XIP_REGION:
  87        case PMSAv8_KERNEL_REGION:
  88                return true;
  89        default:
  90                return false;
  91        }
  92}
  93
  94void __init pmsav8_adjust_lowmem_bounds(void)
  95{
  96        phys_addr_t mem_end;
  97        phys_addr_t reg_start, reg_end;
  98        bool first = true;
  99        u64 i;
 100
 101        for_each_mem_range(i, &reg_start, &reg_end) {
 102                if (first) {
 103                        phys_addr_t phys_offset = PHYS_OFFSET;
 104
 105                        /*
 106                         * Initially only use memory continuous from
 107                         * PHYS_OFFSET */
 108                        if (reg_start != phys_offset)
 109                                panic("First memory bank must be contiguous from PHYS_OFFSET");
 110                        mem_end = reg_end;
 111                        first = false;
 112                } else {
 113                        /*
 114                         * memblock auto merges contiguous blocks, remove
 115                         * all blocks afterwards in one go (we can't remove
 116                         * blocks separately while iterating)
 117                         */
 118                        pr_notice("Ignoring RAM after %pa, memory at %pa ignored\n",
 119                                  &mem_end, &reg_start);
 120                        memblock_remove(reg_start, 0 - reg_start);
 121                        break;
 122                }
 123        }
 124}
 125
 126static int __init __mpu_max_regions(void)
 127{
 128        static int max_regions;
 129        u32 mpuir;
 130
 131        if (max_regions)
 132                return max_regions;
 133
 134        mpuir = read_cpuid_mputype();
 135
 136        max_regions  = (mpuir & MPUIR_DREGION_SZMASK) >> MPUIR_DREGION;
 137
 138        return max_regions;
 139}
 140
 141static int __init __pmsav8_setup_region(unsigned int number, u32 bar, u32 lar)
 142{
 143        if (number > mpu_max_regions
 144            || number >= MPU_MAX_REGIONS)
 145                return -ENOENT;
 146
 147        dsb();
 148        prsel_write(number);
 149        isb();
 150        prbar_write(bar);
 151        prlar_write(lar);
 152
 153        mpu_rgn_info.rgns[number].prbar = bar;
 154        mpu_rgn_info.rgns[number].prlar = lar;
 155
 156        mpu_rgn_info.used++;
 157
 158        return 0;
 159}
 160
 161static int __init pmsav8_setup_ram(unsigned int number, phys_addr_t start,phys_addr_t end)
 162{
 163        u32 bar, lar;
 164
 165        if (is_region_fixed(number))
 166                return -EINVAL;
 167
 168        bar = start;
 169        lar = (end - 1) & ~(PMSAv8_MINALIGN - 1);
 170
 171        bar |= PMSAv8_AP_PL1RW_PL0RW | PMSAv8_RGN_SHARED;
 172        lar |= PMSAv8_LAR_IDX(PMSAv8_RGN_NORMAL) | PMSAv8_LAR_EN;
 173
 174        return __pmsav8_setup_region(number, bar, lar);
 175}
 176
 177static int __init pmsav8_setup_io(unsigned int number, phys_addr_t start,phys_addr_t end)
 178{
 179        u32 bar, lar;
 180
 181        if (is_region_fixed(number))
 182                return -EINVAL;
 183
 184        bar = start;
 185        lar = (end - 1) & ~(PMSAv8_MINALIGN - 1);
 186
 187        bar |= PMSAv8_AP_PL1RW_PL0RW | PMSAv8_RGN_SHARED | PMSAv8_BAR_XN;
 188        lar |= PMSAv8_LAR_IDX(PMSAv8_RGN_DEVICE_nGnRnE) | PMSAv8_LAR_EN;
 189
 190        return __pmsav8_setup_region(number, bar, lar);
 191}
 192
 193static int __init pmsav8_setup_fixed(unsigned int number, phys_addr_t start,phys_addr_t end)
 194{
 195        u32 bar, lar;
 196
 197        if (!is_region_fixed(number))
 198                return -EINVAL;
 199
 200        bar = start;
 201        lar = (end - 1) & ~(PMSAv8_MINALIGN - 1);
 202
 203        bar |= PMSAv8_AP_PL1RW_PL0NA | PMSAv8_RGN_SHARED;
 204        lar |= PMSAv8_LAR_IDX(PMSAv8_RGN_NORMAL) | PMSAv8_LAR_EN;
 205
 206        prsel_write(number);
 207        isb();
 208
 209        if (prbar_read() != bar || prlar_read() != lar)
 210                return -EINVAL;
 211
 212        /* Reserved region was set up early, we just need a record for secondaries */
 213        mpu_rgn_info.rgns[number].prbar = bar;
 214        mpu_rgn_info.rgns[number].prlar = lar;
 215
 216        mpu_rgn_info.used++;
 217
 218        return 0;
 219}
 220
 221#ifndef CONFIG_CPU_V7M
 222static int __init pmsav8_setup_vector(unsigned int number, phys_addr_t start,phys_addr_t end)
 223{
 224        u32 bar, lar;
 225
 226        if (number == PMSAv8_KERNEL_REGION)
 227                return -EINVAL;
 228
 229        bar = start;
 230        lar = (end - 1) & ~(PMSAv8_MINALIGN - 1);
 231
 232        bar |= PMSAv8_AP_PL1RW_PL0NA | PMSAv8_RGN_SHARED;
 233        lar |= PMSAv8_LAR_IDX(PMSAv8_RGN_NORMAL) | PMSAv8_LAR_EN;
 234
 235        return __pmsav8_setup_region(number, bar, lar);
 236}
 237#endif
 238
 239void __init pmsav8_setup(void)
 240{
 241        int i, err = 0;
 242        int region = PMSAv8_KERNEL_REGION;
 243
 244        /* How many regions are supported ? */
 245        mpu_max_regions = __mpu_max_regions();
 246
 247        /* RAM: single chunk of memory */
 248        add_range(mem,  ARRAY_SIZE(mem), 0,  memblock.memory.regions[0].base,
 249                  memblock.memory.regions[0].base + memblock.memory.regions[0].size);
 250
 251        /* IO: cover full 4G range */
 252        add_range(io, ARRAY_SIZE(io), 0, 0, 0xffffffff);
 253
 254        /* RAM and IO: exclude kernel */
 255        subtract_range(mem, ARRAY_SIZE(mem), __pa(KERNEL_START), __pa(KERNEL_END));
 256        subtract_range(io, ARRAY_SIZE(io),  __pa(KERNEL_START), __pa(KERNEL_END));
 257
 258#ifdef CONFIG_XIP_KERNEL
 259        /* RAM and IO: exclude xip */
 260        subtract_range(mem, ARRAY_SIZE(mem), CONFIG_XIP_PHYS_ADDR, __pa(_exiprom));
 261        subtract_range(io, ARRAY_SIZE(io), CONFIG_XIP_PHYS_ADDR, __pa(_exiprom));
 262#endif
 263
 264#ifndef CONFIG_CPU_V7M
 265        /* RAM and IO: exclude vectors */
 266        subtract_range(mem, ARRAY_SIZE(mem),  vectors_base, vectors_base + 2 * PAGE_SIZE);
 267        subtract_range(io, ARRAY_SIZE(io),  vectors_base, vectors_base + 2 * PAGE_SIZE);
 268#endif
 269        /* IO: exclude RAM */
 270        for (i = 0; i < ARRAY_SIZE(mem); i++)
 271                subtract_range(io, ARRAY_SIZE(io), mem[i].start, mem[i].end);
 272
 273        /* Now program MPU */
 274
 275#ifdef CONFIG_XIP_KERNEL
 276        /* ROM */
 277        err |= pmsav8_setup_fixed(PMSAv8_XIP_REGION, CONFIG_XIP_PHYS_ADDR, __pa(_exiprom));
 278#endif
 279        /* Kernel */
 280        err |= pmsav8_setup_fixed(region++, __pa(KERNEL_START), __pa(KERNEL_END));
 281
 282
 283        /* IO */
 284        for (i = 0; i < ARRAY_SIZE(io); i++) {
 285                if (!io[i].end)
 286                        continue;
 287
 288                err |= pmsav8_setup_io(region++, io[i].start, io[i].end);
 289        }
 290
 291        /* RAM */
 292        for (i = 0; i < ARRAY_SIZE(mem); i++) {
 293                if (!mem[i].end)
 294                        continue;
 295
 296                err |= pmsav8_setup_ram(region++, mem[i].start, mem[i].end);
 297        }
 298
 299        /* Vectors */
 300#ifndef CONFIG_CPU_V7M
 301        err |= pmsav8_setup_vector(region++, vectors_base, vectors_base + 2 * PAGE_SIZE);
 302#endif
 303        if (err)
 304                pr_warn("MPU region initialization failure! %d", err);
 305        else
 306                pr_info("Using ARM PMSAv8 Compliant MPU. Used %d of %d regions\n",
 307                        mpu_rgn_info.used, mpu_max_regions);
 308}
 309