Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 1 | /* |
| 2 | * Based on linux/arch/arm/mm/nommu.c |
| 3 | * |
| 4 | * ARM PMSAv7 supporting functions. |
| 5 | */ |
| 6 | |
Vladimir Murzin | 5c9d9a1 | 2017-10-16 12:59:15 +0100 | [diff] [blame] | 7 | #include <linux/bitops.h> |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 8 | #include <linux/memblock.h> |
Vladimir Murzin | 636e645 | 2018-01-03 10:09:33 +0100 | [diff] [blame] | 9 | #include <linux/string.h> |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 10 | |
Vladimir Murzin | 2162183 | 2017-10-16 13:00:45 +0100 | [diff] [blame] | 11 | #include <asm/cacheflush.h> |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 12 | #include <asm/cp15.h> |
| 13 | #include <asm/cputype.h> |
| 14 | #include <asm/mpu.h> |
Vladimir Murzin | 2162183 | 2017-10-16 13:00:45 +0100 | [diff] [blame] | 15 | #include <asm/sections.h> |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 16 | |
| 17 | #include "mm.h" |
| 18 | |
Vladimir Murzin | 5c9d9a1 | 2017-10-16 12:59:15 +0100 | [diff] [blame] | 19 | struct region { |
| 20 | phys_addr_t base; |
| 21 | phys_addr_t size; |
| 22 | unsigned long subreg; |
| 23 | }; |
| 24 | |
| 25 | static struct region __initdata mem[MPU_MAX_REGIONS]; |
Vladimir Murzin | 2162183 | 2017-10-16 13:00:45 +0100 | [diff] [blame] | 26 | #ifdef CONFIG_XIP_KERNEL |
| 27 | static struct region __initdata xip[MPU_MAX_REGIONS]; |
| 28 | #endif |
Vladimir Murzin | 5c9d9a1 | 2017-10-16 12:59:15 +0100 | [diff] [blame] | 29 | |
Vladimir Murzin | a0995c08 | 2017-10-16 12:54:05 +0100 | [diff] [blame] | 30 | static unsigned int __initdata mpu_min_region_order; |
| 31 | static unsigned int __initdata mpu_max_regions; |
| 32 | |
Vladimir Murzin | 5c9d9a1 | 2017-10-16 12:59:15 +0100 | [diff] [blame] | 33 | static int __init __mpu_min_region_order(void); |
| 34 | static int __init __mpu_max_regions(void); |
| 35 | |
Vladimir Murzin | 9fcb01a | 2017-10-16 12:57:48 +0100 | [diff] [blame] | 36 | #ifndef CONFIG_CPU_V7M |
| 37 | |
Vladimir Murzin | e8b47e1 | 2017-10-16 12:53:18 +0100 | [diff] [blame] | 38 | #define DRBAR __ACCESS_CP15(c6, 0, c1, 0) |
| 39 | #define IRBAR __ACCESS_CP15(c6, 0, c1, 1) |
| 40 | #define DRSR __ACCESS_CP15(c6, 0, c1, 2) |
| 41 | #define IRSR __ACCESS_CP15(c6, 0, c1, 3) |
| 42 | #define DRACR __ACCESS_CP15(c6, 0, c1, 4) |
| 43 | #define IRACR __ACCESS_CP15(c6, 0, c1, 5) |
| 44 | #define RNGNR __ACCESS_CP15(c6, 0, c2, 0) |
| 45 | |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 46 | /* Region number */ |
Vladimir Murzin | e8b47e1 | 2017-10-16 12:53:18 +0100 | [diff] [blame] | 47 | static inline void rgnr_write(u32 v) |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 48 | { |
Vladimir Murzin | e8b47e1 | 2017-10-16 12:53:18 +0100 | [diff] [blame] | 49 | write_sysreg(v, RNGNR); |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 50 | } |
| 51 | |
| 52 | /* Data-side / unified region attributes */ |
| 53 | |
| 54 | /* Region access control register */ |
Vladimir Murzin | e8b47e1 | 2017-10-16 12:53:18 +0100 | [diff] [blame] | 55 | static inline void dracr_write(u32 v) |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 56 | { |
Vladimir Murzin | e8b47e1 | 2017-10-16 12:53:18 +0100 | [diff] [blame] | 57 | write_sysreg(v, DRACR); |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 58 | } |
| 59 | |
| 60 | /* Region size register */ |
Vladimir Murzin | e8b47e1 | 2017-10-16 12:53:18 +0100 | [diff] [blame] | 61 | static inline void drsr_write(u32 v) |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 62 | { |
Vladimir Murzin | e8b47e1 | 2017-10-16 12:53:18 +0100 | [diff] [blame] | 63 | write_sysreg(v, DRSR); |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 64 | } |
| 65 | |
| 66 | /* Region base address register */ |
Vladimir Murzin | e8b47e1 | 2017-10-16 12:53:18 +0100 | [diff] [blame] | 67 | static inline void drbar_write(u32 v) |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 68 | { |
Vladimir Murzin | e8b47e1 | 2017-10-16 12:53:18 +0100 | [diff] [blame] | 69 | write_sysreg(v, DRBAR); |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 70 | } |
| 71 | |
Vladimir Murzin | e8b47e1 | 2017-10-16 12:53:18 +0100 | [diff] [blame] | 72 | static inline u32 drbar_read(void) |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 73 | { |
Vladimir Murzin | e8b47e1 | 2017-10-16 12:53:18 +0100 | [diff] [blame] | 74 | return read_sysreg(DRBAR); |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 75 | } |
| 76 | /* Optional instruction-side region attributes */ |
| 77 | |
| 78 | /* I-side Region access control register */ |
Vladimir Murzin | e8b47e1 | 2017-10-16 12:53:18 +0100 | [diff] [blame] | 79 | static inline void iracr_write(u32 v) |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 80 | { |
Vladimir Murzin | e8b47e1 | 2017-10-16 12:53:18 +0100 | [diff] [blame] | 81 | write_sysreg(v, IRACR); |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 82 | } |
| 83 | |
| 84 | /* I-side Region size register */ |
Vladimir Murzin | e8b47e1 | 2017-10-16 12:53:18 +0100 | [diff] [blame] | 85 | static inline void irsr_write(u32 v) |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 86 | { |
Vladimir Murzin | e8b47e1 | 2017-10-16 12:53:18 +0100 | [diff] [blame] | 87 | write_sysreg(v, IRSR); |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 88 | } |
| 89 | |
| 90 | /* I-side Region base address register */ |
Vladimir Murzin | e8b47e1 | 2017-10-16 12:53:18 +0100 | [diff] [blame] | 91 | static inline void irbar_write(u32 v) |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 92 | { |
Vladimir Murzin | e8b47e1 | 2017-10-16 12:53:18 +0100 | [diff] [blame] | 93 | write_sysreg(v, IRBAR); |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 94 | } |
| 95 | |
Vladimir Murzin | e8b47e1 | 2017-10-16 12:53:18 +0100 | [diff] [blame] | 96 | static inline u32 irbar_read(void) |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 97 | { |
Vladimir Murzin | e8b47e1 | 2017-10-16 12:53:18 +0100 | [diff] [blame] | 98 | return read_sysreg(IRBAR); |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 99 | } |
| 100 | |
Vladimir Murzin | 9fcb01a | 2017-10-16 12:57:48 +0100 | [diff] [blame] | 101 | #else |
| 102 | |
| 103 | static inline void rgnr_write(u32 v) |
| 104 | { |
Vladimir Murzin | 9cfb541 | 2018-04-03 10:36:37 +0100 | [diff] [blame] | 105 | writel_relaxed(v, BASEADDR_V7M_SCB + PMSAv7_RNR); |
Vladimir Murzin | 9fcb01a | 2017-10-16 12:57:48 +0100 | [diff] [blame] | 106 | } |
| 107 | |
| 108 | /* Data-side / unified region attributes */ |
| 109 | |
| 110 | /* Region access control register */ |
| 111 | static inline void dracr_write(u32 v) |
| 112 | { |
Vladimir Murzin | 9cfb541 | 2018-04-03 10:36:37 +0100 | [diff] [blame] | 113 | u32 rsr = readl_relaxed(BASEADDR_V7M_SCB + PMSAv7_RASR) & GENMASK(15, 0); |
Vladimir Murzin | 9fcb01a | 2017-10-16 12:57:48 +0100 | [diff] [blame] | 114 | |
Vladimir Murzin | 9cfb541 | 2018-04-03 10:36:37 +0100 | [diff] [blame] | 115 | writel_relaxed((v << 16) | rsr, BASEADDR_V7M_SCB + PMSAv7_RASR); |
Vladimir Murzin | 9fcb01a | 2017-10-16 12:57:48 +0100 | [diff] [blame] | 116 | } |
| 117 | |
| 118 | /* Region size register */ |
| 119 | static inline void drsr_write(u32 v) |
| 120 | { |
Vladimir Murzin | 9cfb541 | 2018-04-03 10:36:37 +0100 | [diff] [blame] | 121 | u32 racr = readl_relaxed(BASEADDR_V7M_SCB + PMSAv7_RASR) & GENMASK(31, 16); |
Vladimir Murzin | 9fcb01a | 2017-10-16 12:57:48 +0100 | [diff] [blame] | 122 | |
Vladimir Murzin | 9cfb541 | 2018-04-03 10:36:37 +0100 | [diff] [blame] | 123 | writel_relaxed(v | racr, BASEADDR_V7M_SCB + PMSAv7_RASR); |
Vladimir Murzin | 9fcb01a | 2017-10-16 12:57:48 +0100 | [diff] [blame] | 124 | } |
| 125 | |
| 126 | /* Region base address register */ |
| 127 | static inline void drbar_write(u32 v) |
| 128 | { |
Vladimir Murzin | 9cfb541 | 2018-04-03 10:36:37 +0100 | [diff] [blame] | 129 | writel_relaxed(v, BASEADDR_V7M_SCB + PMSAv7_RBAR); |
Vladimir Murzin | 9fcb01a | 2017-10-16 12:57:48 +0100 | [diff] [blame] | 130 | } |
| 131 | |
| 132 | static inline u32 drbar_read(void) |
| 133 | { |
Vladimir Murzin | 9cfb541 | 2018-04-03 10:36:37 +0100 | [diff] [blame] | 134 | return readl_relaxed(BASEADDR_V7M_SCB + PMSAv7_RBAR); |
Vladimir Murzin | 9fcb01a | 2017-10-16 12:57:48 +0100 | [diff] [blame] | 135 | } |
| 136 | |
| 137 | /* ARMv7-M only supports a unified MPU, so I-side operations are nop */ |
| 138 | |
| 139 | static inline void iracr_write(u32 v) {} |
| 140 | static inline void irsr_write(u32 v) {} |
| 141 | static inline void irbar_write(u32 v) {} |
| 142 | static inline unsigned long irbar_read(void) {return 0;} |
| 143 | |
| 144 | #endif |
| 145 | |
Vladimir Murzin | 5c9d9a1 | 2017-10-16 12:59:15 +0100 | [diff] [blame] | 146 | static bool __init try_split_region(phys_addr_t base, phys_addr_t size, struct region *region) |
| 147 | { |
| 148 | unsigned long subreg, bslots, sslots; |
| 149 | phys_addr_t abase = base & ~(size - 1); |
| 150 | phys_addr_t asize = base + size - abase; |
| 151 | phys_addr_t p2size = 1 << __fls(asize); |
| 152 | phys_addr_t bdiff, sdiff; |
| 153 | |
| 154 | if (p2size != asize) |
| 155 | p2size *= 2; |
| 156 | |
| 157 | bdiff = base - abase; |
| 158 | sdiff = p2size - asize; |
Vladimir Murzin | 9cfb541 | 2018-04-03 10:36:37 +0100 | [diff] [blame] | 159 | subreg = p2size / PMSAv7_NR_SUBREGS; |
Vladimir Murzin | 5c9d9a1 | 2017-10-16 12:59:15 +0100 | [diff] [blame] | 160 | |
| 161 | if ((bdiff % subreg) || (sdiff % subreg)) |
| 162 | return false; |
| 163 | |
| 164 | bslots = bdiff / subreg; |
| 165 | sslots = sdiff / subreg; |
| 166 | |
| 167 | if (bslots || sslots) { |
| 168 | int i; |
| 169 | |
Vladimir Murzin | 9cfb541 | 2018-04-03 10:36:37 +0100 | [diff] [blame] | 170 | if (subreg < PMSAv7_MIN_SUBREG_SIZE) |
Vladimir Murzin | 5c9d9a1 | 2017-10-16 12:59:15 +0100 | [diff] [blame] | 171 | return false; |
| 172 | |
Vladimir Murzin | 9cfb541 | 2018-04-03 10:36:37 +0100 | [diff] [blame] | 173 | if (bslots + sslots > PMSAv7_NR_SUBREGS) |
Vladimir Murzin | 5c9d9a1 | 2017-10-16 12:59:15 +0100 | [diff] [blame] | 174 | return false; |
| 175 | |
| 176 | for (i = 0; i < bslots; i++) |
| 177 | _set_bit(i, ®ion->subreg); |
| 178 | |
| 179 | for (i = 1; i <= sslots; i++) |
Vladimir Murzin | 9cfb541 | 2018-04-03 10:36:37 +0100 | [diff] [blame] | 180 | _set_bit(PMSAv7_NR_SUBREGS - i, ®ion->subreg); |
Vladimir Murzin | 5c9d9a1 | 2017-10-16 12:59:15 +0100 | [diff] [blame] | 181 | } |
| 182 | |
| 183 | region->base = abase; |
| 184 | region->size = p2size; |
| 185 | |
| 186 | return true; |
| 187 | } |
| 188 | |
| 189 | static int __init allocate_region(phys_addr_t base, phys_addr_t size, |
| 190 | unsigned int limit, struct region *regions) |
| 191 | { |
| 192 | int count = 0; |
| 193 | phys_addr_t diff = size; |
| 194 | int attempts = MPU_MAX_REGIONS; |
| 195 | |
| 196 | while (diff) { |
| 197 | /* Try cover region as is (maybe with help of subregions) */ |
| 198 | if (try_split_region(base, size, ®ions[count])) { |
| 199 | count++; |
| 200 | base += size; |
| 201 | diff -= size; |
| 202 | size = diff; |
| 203 | } else { |
| 204 | /* |
| 205 | * Maximum aligned region might overflow phys_addr_t |
| 206 | * if "base" is 0. Hence we keep everything below 4G |
| 207 | * until we take the smaller of the aligned region |
| 208 | * size ("asize") and rounded region size ("p2size"), |
| 209 | * one of which is guaranteed to be smaller than the |
| 210 | * maximum physical address. |
| 211 | */ |
| 212 | phys_addr_t asize = (base - 1) ^ base; |
| 213 | phys_addr_t p2size = (1 << __fls(diff)) - 1; |
| 214 | |
| 215 | size = asize < p2size ? asize + 1 : p2size + 1; |
| 216 | } |
| 217 | |
| 218 | if (count > limit) |
| 219 | break; |
| 220 | |
| 221 | if (!attempts) |
| 222 | break; |
| 223 | |
| 224 | attempts--; |
| 225 | } |
| 226 | |
| 227 | return count; |
| 228 | } |
| 229 | |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 230 | /* MPU initialisation functions */ |
Vladimir Murzin | 9cfb541 | 2018-04-03 10:36:37 +0100 | [diff] [blame] | 231 | void __init pmsav7_adjust_lowmem_bounds(void) |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 232 | { |
Arnd Bergmann | fe9c058 | 2017-11-02 13:20:31 +0100 | [diff] [blame] | 233 | phys_addr_t specified_mem_size = 0, total_mem_size = 0; |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 234 | struct memblock_region *reg; |
| 235 | bool first = true; |
| 236 | phys_addr_t mem_start; |
| 237 | phys_addr_t mem_end; |
Vladimir Murzin | 5c9d9a1 | 2017-10-16 12:59:15 +0100 | [diff] [blame] | 238 | unsigned int mem_max_regions; |
| 239 | int num, i; |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 240 | |
Vladimir Murzin | 9cfb541 | 2018-04-03 10:36:37 +0100 | [diff] [blame] | 241 | /* Free-up PMSAv7_PROBE_REGION */ |
Vladimir Murzin | 5c9d9a1 | 2017-10-16 12:59:15 +0100 | [diff] [blame] | 242 | mpu_min_region_order = __mpu_min_region_order(); |
| 243 | |
| 244 | /* How many regions are supported */ |
| 245 | mpu_max_regions = __mpu_max_regions(); |
| 246 | |
| 247 | mem_max_regions = min((unsigned int)MPU_MAX_REGIONS, mpu_max_regions); |
| 248 | |
| 249 | /* We need to keep one slot for background region */ |
| 250 | mem_max_regions--; |
| 251 | |
| 252 | #ifndef CONFIG_CPU_V7M |
| 253 | /* ... and one for vectors */ |
| 254 | mem_max_regions--; |
| 255 | #endif |
Vladimir Murzin | 2162183 | 2017-10-16 13:00:45 +0100 | [diff] [blame] | 256 | |
| 257 | #ifdef CONFIG_XIP_KERNEL |
| 258 | /* plus some regions to cover XIP ROM */ |
| 259 | num = allocate_region(CONFIG_XIP_PHYS_ADDR, __pa(_exiprom) - CONFIG_XIP_PHYS_ADDR, |
| 260 | mem_max_regions, xip); |
| 261 | |
| 262 | mem_max_regions -= num; |
| 263 | #endif |
| 264 | |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 265 | for_each_memblock(memory, reg) { |
| 266 | if (first) { |
Vladimir Murzin | 2162183 | 2017-10-16 13:00:45 +0100 | [diff] [blame] | 267 | phys_addr_t phys_offset = PHYS_OFFSET; |
| 268 | |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 269 | /* |
| 270 | * Initially only use memory continuous from |
| 271 | * PHYS_OFFSET */ |
| 272 | if (reg->base != phys_offset) |
| 273 | panic("First memory bank must be contiguous from PHYS_OFFSET"); |
| 274 | |
| 275 | mem_start = reg->base; |
| 276 | mem_end = reg->base + reg->size; |
| 277 | specified_mem_size = reg->size; |
| 278 | first = false; |
| 279 | } else { |
| 280 | /* |
| 281 | * memblock auto merges contiguous blocks, remove |
| 282 | * all blocks afterwards in one go (we can't remove |
| 283 | * blocks separately while iterating) |
| 284 | */ |
| 285 | pr_notice("Ignoring RAM after %pa, memory at %pa ignored\n", |
| 286 | &mem_end, ®->base); |
| 287 | memblock_remove(reg->base, 0 - reg->base); |
| 288 | break; |
| 289 | } |
| 290 | } |
| 291 | |
Vladimir Murzin | 636e645 | 2018-01-03 10:09:33 +0100 | [diff] [blame] | 292 | memset(mem, 0, sizeof(mem)); |
Vladimir Murzin | 5c9d9a1 | 2017-10-16 12:59:15 +0100 | [diff] [blame] | 293 | num = allocate_region(mem_start, specified_mem_size, mem_max_regions, mem); |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 294 | |
Vladimir Murzin | 5c9d9a1 | 2017-10-16 12:59:15 +0100 | [diff] [blame] | 295 | for (i = 0; i < num; i++) { |
Vladimir Murzin | 9cfb541 | 2018-04-03 10:36:37 +0100 | [diff] [blame] | 296 | unsigned long subreg = mem[i].size / PMSAv7_NR_SUBREGS; |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 297 | |
Vladimir Murzin | 5c9d9a1 | 2017-10-16 12:59:15 +0100 | [diff] [blame] | 298 | total_mem_size += mem[i].size - subreg * hweight_long(mem[i].subreg); |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 299 | |
Vladimir Murzin | 5c9d9a1 | 2017-10-16 12:59:15 +0100 | [diff] [blame] | 300 | pr_debug("MPU: base %pa size %pa disable subregions: %*pbl\n", |
Vladimir Murzin | 9cfb541 | 2018-04-03 10:36:37 +0100 | [diff] [blame] | 301 | &mem[i].base, &mem[i].size, PMSAv7_NR_SUBREGS, &mem[i].subreg); |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 302 | } |
| 303 | |
Vladimir Murzin | 5c9d9a1 | 2017-10-16 12:59:15 +0100 | [diff] [blame] | 304 | if (total_mem_size != specified_mem_size) { |
| 305 | pr_warn("Truncating memory from %pa to %pa (MPU region constraints)", |
| 306 | &specified_mem_size, &total_mem_size); |
| 307 | memblock_remove(mem_start + total_mem_size, |
| 308 | specified_mem_size - total_mem_size); |
| 309 | } |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 310 | } |
| 311 | |
Vladimir Murzin | a0995c08 | 2017-10-16 12:54:05 +0100 | [diff] [blame] | 312 | static int __init __mpu_max_regions(void) |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 313 | { |
| 314 | /* |
| 315 | * We don't support a different number of I/D side regions so if we |
| 316 | * have separate instruction and data memory maps then return |
| 317 | * whichever side has a smaller number of supported regions. |
| 318 | */ |
| 319 | u32 dregions, iregions, mpuir; |
Vladimir Murzin | a0995c08 | 2017-10-16 12:54:05 +0100 | [diff] [blame] | 320 | |
Vladimir Murzin | 9fcb01a | 2017-10-16 12:57:48 +0100 | [diff] [blame] | 321 | mpuir = read_cpuid_mputype(); |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 322 | |
| 323 | dregions = iregions = (mpuir & MPUIR_DREGION_SZMASK) >> MPUIR_DREGION; |
| 324 | |
| 325 | /* Check for separate d-side and i-side memory maps */ |
| 326 | if (mpuir & MPUIR_nU) |
| 327 | iregions = (mpuir & MPUIR_IREGION_SZMASK) >> MPUIR_IREGION; |
| 328 | |
| 329 | /* Use the smallest of the two maxima */ |
| 330 | return min(dregions, iregions); |
| 331 | } |
| 332 | |
Vladimir Murzin | a0995c08 | 2017-10-16 12:54:05 +0100 | [diff] [blame] | 333 | static int __init mpu_iside_independent(void) |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 334 | { |
| 335 | /* MPUIR.nU specifies whether there is *not* a unified memory map */ |
Vladimir Murzin | 9fcb01a | 2017-10-16 12:57:48 +0100 | [diff] [blame] | 336 | return read_cpuid_mputype() & MPUIR_nU; |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 337 | } |
| 338 | |
Vladimir Murzin | a0995c08 | 2017-10-16 12:54:05 +0100 | [diff] [blame] | 339 | static int __init __mpu_min_region_order(void) |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 340 | { |
| 341 | u32 drbar_result, irbar_result; |
Vladimir Murzin | a0995c08 | 2017-10-16 12:54:05 +0100 | [diff] [blame] | 342 | |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 343 | /* We've kept a region free for this probing */ |
Vladimir Murzin | 9cfb541 | 2018-04-03 10:36:37 +0100 | [diff] [blame] | 344 | rgnr_write(PMSAv7_PROBE_REGION); |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 345 | isb(); |
| 346 | /* |
| 347 | * As per ARM ARM, write 0xFFFFFFFC to DRBAR to find the minimum |
| 348 | * region order |
| 349 | */ |
| 350 | drbar_write(0xFFFFFFFC); |
| 351 | drbar_result = irbar_result = drbar_read(); |
| 352 | drbar_write(0x0); |
| 353 | /* If the MPU is non-unified, we use the larger of the two minima*/ |
| 354 | if (mpu_iside_independent()) { |
| 355 | irbar_write(0xFFFFFFFC); |
| 356 | irbar_result = irbar_read(); |
| 357 | irbar_write(0x0); |
| 358 | } |
| 359 | isb(); /* Ensure that MPU region operations have completed */ |
| 360 | /* Return whichever result is larger */ |
Vladimir Murzin | a0995c08 | 2017-10-16 12:54:05 +0100 | [diff] [blame] | 361 | |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 362 | return __ffs(max(drbar_result, irbar_result)); |
| 363 | } |
| 364 | |
Vladimir Murzin | a0995c08 | 2017-10-16 12:54:05 +0100 | [diff] [blame] | 365 | static int __init mpu_setup_region(unsigned int number, phys_addr_t start, |
Vladimir Murzin | 5c9d9a1 | 2017-10-16 12:59:15 +0100 | [diff] [blame] | 366 | unsigned int size_order, unsigned int properties, |
Vladimir Murzin | 2162183 | 2017-10-16 13:00:45 +0100 | [diff] [blame] | 367 | unsigned int subregions, bool need_flush) |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 368 | { |
| 369 | u32 size_data; |
| 370 | |
| 371 | /* We kept a region free for probing resolution of MPU regions*/ |
Vladimir Murzin | a0995c08 | 2017-10-16 12:54:05 +0100 | [diff] [blame] | 372 | if (number > mpu_max_regions |
| 373 | || number >= MPU_MAX_REGIONS) |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 374 | return -ENOENT; |
| 375 | |
| 376 | if (size_order > 32) |
| 377 | return -ENOMEM; |
| 378 | |
Vladimir Murzin | a0995c08 | 2017-10-16 12:54:05 +0100 | [diff] [blame] | 379 | if (size_order < mpu_min_region_order) |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 380 | return -ENOMEM; |
| 381 | |
| 382 | /* Writing N to bits 5:1 (RSR_SZ) specifies region size 2^N+1 */ |
Vladimir Murzin | 9cfb541 | 2018-04-03 10:36:37 +0100 | [diff] [blame] | 383 | size_data = ((size_order - 1) << PMSAv7_RSR_SZ) | 1 << PMSAv7_RSR_EN; |
| 384 | size_data |= subregions << PMSAv7_RSR_SD; |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 385 | |
Vladimir Murzin | 2162183 | 2017-10-16 13:00:45 +0100 | [diff] [blame] | 386 | if (need_flush) |
| 387 | flush_cache_all(); |
| 388 | |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 389 | dsb(); /* Ensure all previous data accesses occur with old mappings */ |
| 390 | rgnr_write(number); |
| 391 | isb(); |
| 392 | drbar_write(start); |
| 393 | dracr_write(properties); |
| 394 | isb(); /* Propagate properties before enabling region */ |
| 395 | drsr_write(size_data); |
| 396 | |
| 397 | /* Check for independent I-side registers */ |
| 398 | if (mpu_iside_independent()) { |
| 399 | irbar_write(start); |
| 400 | iracr_write(properties); |
| 401 | isb(); |
| 402 | irsr_write(size_data); |
| 403 | } |
| 404 | isb(); |
| 405 | |
| 406 | /* Store region info (we treat i/d side the same, so only store d) */ |
| 407 | mpu_rgn_info.rgns[number].dracr = properties; |
| 408 | mpu_rgn_info.rgns[number].drbar = start; |
| 409 | mpu_rgn_info.rgns[number].drsr = size_data; |
Vladimir Murzin | a0995c08 | 2017-10-16 12:54:05 +0100 | [diff] [blame] | 410 | |
| 411 | mpu_rgn_info.used++; |
| 412 | |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 413 | return 0; |
| 414 | } |
| 415 | |
| 416 | /* |
| 417 | * Set up default MPU regions, doing nothing if there is no MPU |
| 418 | */ |
Vladimir Murzin | 9cfb541 | 2018-04-03 10:36:37 +0100 | [diff] [blame] | 419 | void __init pmsav7_setup(void) |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 420 | { |
Vladimir Murzin | 5c9d9a1 | 2017-10-16 12:59:15 +0100 | [diff] [blame] | 421 | int i, region = 0, err = 0; |
Vladimir Murzin | a0995c08 | 2017-10-16 12:54:05 +0100 | [diff] [blame] | 422 | |
Vladimir Murzin | 5c9d9a1 | 2017-10-16 12:59:15 +0100 | [diff] [blame] | 423 | /* Setup MPU (order is important) */ |
Vladimir Murzin | a0995c08 | 2017-10-16 12:54:05 +0100 | [diff] [blame] | 424 | |
| 425 | /* Background */ |
| 426 | err |= mpu_setup_region(region++, 0, 32, |
Vladimir Murzin | 9cfb541 | 2018-04-03 10:36:37 +0100 | [diff] [blame] | 427 | PMSAv7_ACR_XN | PMSAv7_RGN_STRONGLY_ORDERED | PMSAv7_AP_PL1RW_PL0RW, |
Vladimir Murzin | 2162183 | 2017-10-16 13:00:45 +0100 | [diff] [blame] | 428 | 0, false); |
| 429 | |
| 430 | #ifdef CONFIG_XIP_KERNEL |
| 431 | /* ROM */ |
| 432 | for (i = 0; i < ARRAY_SIZE(xip); i++) { |
| 433 | /* |
| 434 | * In case we overwrite RAM region we set earlier in |
| 435 | * head-nommu.S (which is cachable) all subsequent |
| 436 | * data access till we setup RAM bellow would be done |
| 437 | * with BG region (which is uncachable), thus we need |
| 438 | * to clean and invalidate cache. |
| 439 | */ |
Vladimir Murzin | 9cfb541 | 2018-04-03 10:36:37 +0100 | [diff] [blame] | 440 | bool need_flush = region == PMSAv7_RAM_REGION; |
Vladimir Murzin | 2162183 | 2017-10-16 13:00:45 +0100 | [diff] [blame] | 441 | |
| 442 | if (!xip[i].size) |
| 443 | continue; |
| 444 | |
| 445 | err |= mpu_setup_region(region++, xip[i].base, ilog2(xip[i].size), |
Vladimir Murzin | 9cfb541 | 2018-04-03 10:36:37 +0100 | [diff] [blame] | 446 | PMSAv7_AP_PL1RO_PL0NA | PMSAv7_RGN_NORMAL, |
Vladimir Murzin | 2162183 | 2017-10-16 13:00:45 +0100 | [diff] [blame] | 447 | xip[i].subreg, need_flush); |
| 448 | } |
| 449 | #endif |
Vladimir Murzin | a0995c08 | 2017-10-16 12:54:05 +0100 | [diff] [blame] | 450 | |
| 451 | /* RAM */ |
Vladimir Murzin | 5c9d9a1 | 2017-10-16 12:59:15 +0100 | [diff] [blame] | 452 | for (i = 0; i < ARRAY_SIZE(mem); i++) { |
| 453 | if (!mem[i].size) |
| 454 | continue; |
| 455 | |
| 456 | err |= mpu_setup_region(region++, mem[i].base, ilog2(mem[i].size), |
Vladimir Murzin | 9cfb541 | 2018-04-03 10:36:37 +0100 | [diff] [blame] | 457 | PMSAv7_AP_PL1RW_PL0RW | PMSAv7_RGN_NORMAL, |
Vladimir Murzin | 2162183 | 2017-10-16 13:00:45 +0100 | [diff] [blame] | 458 | mem[i].subreg, false); |
Vladimir Murzin | 5c9d9a1 | 2017-10-16 12:59:15 +0100 | [diff] [blame] | 459 | } |
Vladimir Murzin | a0995c08 | 2017-10-16 12:54:05 +0100 | [diff] [blame] | 460 | |
| 461 | /* Vectors */ |
Vladimir Murzin | 9fcb01a | 2017-10-16 12:57:48 +0100 | [diff] [blame] | 462 | #ifndef CONFIG_CPU_V7M |
Vladimir Murzin | 5c9d9a1 | 2017-10-16 12:59:15 +0100 | [diff] [blame] | 463 | err |= mpu_setup_region(region++, vectors_base, ilog2(2 * PAGE_SIZE), |
Vladimir Murzin | 9cfb541 | 2018-04-03 10:36:37 +0100 | [diff] [blame] | 464 | PMSAv7_AP_PL1RW_PL0NA | PMSAv7_RGN_NORMAL, |
Vladimir Murzin | 2162183 | 2017-10-16 13:00:45 +0100 | [diff] [blame] | 465 | 0, false); |
Vladimir Murzin | 9fcb01a | 2017-10-16 12:57:48 +0100 | [diff] [blame] | 466 | #endif |
Vladimir Murzin | a0995c08 | 2017-10-16 12:54:05 +0100 | [diff] [blame] | 467 | if (err) { |
| 468 | panic("MPU region initialization failure! %d", err); |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 469 | } else { |
| 470 | pr_info("Using ARMv7 PMSA Compliant MPU. " |
Vladimir Murzin | a0995c08 | 2017-10-16 12:54:05 +0100 | [diff] [blame] | 471 | "Region independence: %s, Used %d of %d regions\n", |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 472 | mpu_iside_independent() ? "Yes" : "No", |
Vladimir Murzin | a0995c08 | 2017-10-16 12:54:05 +0100 | [diff] [blame] | 473 | mpu_rgn_info.used, mpu_max_regions); |
Vladimir Murzin | 877ec11 | 2017-10-16 12:52:35 +0100 | [diff] [blame] | 474 | } |
| 475 | } |