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/page.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 | |
25 | static inline u32 prlar_read(void) |
26 | { |
27 | return read_sysreg(PRLAR); |
28 | } |
29 | |
30 | static inline u32 prbar_read(void) |
31 | { |
32 | return read_sysreg(PRBAR); |
33 | } |
34 | |
35 | static inline void prsel_write(u32 v) |
36 | { |
37 | write_sysreg(v, PRSEL); |
38 | } |
39 | |
40 | static inline void prbar_write(u32 v) |
41 | { |
42 | write_sysreg(v, PRBAR); |
43 | } |
44 | |
45 | static inline void prlar_write(u32 v) |
46 | { |
47 | write_sysreg(v, PRLAR); |
48 | } |
49 | #else |
50 | |
51 | static inline u32 prlar_read(void) |
52 | { |
53 | return readl_relaxed(BASEADDR_V7M_SCB + PMSAv8_RLAR); |
54 | } |
55 | |
56 | static inline u32 prbar_read(void) |
57 | { |
58 | return readl_relaxed(BASEADDR_V7M_SCB + PMSAv8_RBAR); |
59 | } |
60 | |
61 | static inline void prsel_write(u32 v) |
62 | { |
63 | writel_relaxed(v, BASEADDR_V7M_SCB + PMSAv8_RNR); |
64 | } |
65 | |
66 | static inline void prbar_write(u32 v) |
67 | { |
68 | writel_relaxed(v, BASEADDR_V7M_SCB + PMSAv8_RBAR); |
69 | } |
70 | |
71 | static inline void prlar_write(u32 v) |
72 | { |
73 | writel_relaxed(v, BASEADDR_V7M_SCB + PMSAv8_RLAR); |
74 | } |
75 | |
76 | #endif |
77 | |
78 | static struct range __initdata io[MPU_MAX_REGIONS]; |
79 | static struct range __initdata mem[MPU_MAX_REGIONS]; |
80 | |
81 | static unsigned int __initdata mpu_max_regions; |
82 | |
83 | static __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 | |
94 | void __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, ®_start, ®_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(fmt: "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, ®_start); |
120 | memblock_remove(base: reg_start, size: 0 - reg_start); |
121 | break; |
122 | } |
123 | } |
124 | } |
125 | |
126 | static 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 | |
141 | static 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(v: number); |
149 | isb(); |
150 | prbar_write(v: bar); |
151 | prlar_write(v: 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 | |
161 | static 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 | |
177 | static 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 | |
193 | static 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(v: 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 |
222 | static 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 | |
239 | void __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(range: mem, ARRAY_SIZE(mem), nr_range: 0, start: memblock.memory.regions[0].base, |
249 | end: memblock.memory.regions[0].base + memblock.memory.regions[0].size); |
250 | |
251 | /* IO: cover full 4G range */ |
252 | add_range(range: io, ARRAY_SIZE(io), nr_range: 0, start: 0, end: 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(range: io, ARRAY_SIZE(io), start: mem[i].start, end: 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(number: region++, start: io[i].start, end: 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(number: region++, start: mem[i].start, end: 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 | |