diff options
author | Russell King <rmk+kernel@arm.linux.org.uk> | 2013-06-29 06:44:43 -0400 |
---|---|---|
committer | Russell King <rmk+kernel@arm.linux.org.uk> | 2013-06-29 06:44:43 -0400 |
commit | 3c0c01ab742ddfaf6b6f2d64b890e77cda4b7727 (patch) | |
tree | d0f196c53d209f44190fd8a6481823b7770866e6 /arch/arm/mm/nommu.c | |
parent | cbd379b10019617457bda31eb243890f4377fa3e (diff) | |
parent | 809e660f438fc5a69bf57630a85bcd8112263f37 (diff) |
Merge branch 'devel-stable' into for-next
Conflicts:
arch/arm/Makefile
arch/arm/include/asm/glue-proc.h
Diffstat (limited to 'arch/arm/mm/nommu.c')
-rw-r--r-- | arch/arm/mm/nommu.c | 264 |
1 files changed, 263 insertions, 1 deletions
diff --git a/arch/arm/mm/nommu.c b/arch/arm/mm/nommu.c index 7fe0524a5449..1fa50100ab6a 100644 --- a/arch/arm/mm/nommu.c +++ b/arch/arm/mm/nommu.c | |||
@@ -8,6 +8,7 @@ | |||
8 | #include <linux/pagemap.h> | 8 | #include <linux/pagemap.h> |
9 | #include <linux/io.h> | 9 | #include <linux/io.h> |
10 | #include <linux/memblock.h> | 10 | #include <linux/memblock.h> |
11 | #include <linux/kernel.h> | ||
11 | 12 | ||
12 | #include <asm/cacheflush.h> | 13 | #include <asm/cacheflush.h> |
13 | #include <asm/sections.h> | 14 | #include <asm/sections.h> |
@@ -15,22 +16,282 @@ | |||
15 | #include <asm/setup.h> | 16 | #include <asm/setup.h> |
16 | #include <asm/traps.h> | 17 | #include <asm/traps.h> |
17 | #include <asm/mach/arch.h> | 18 | #include <asm/mach/arch.h> |
19 | #include <asm/cputype.h> | ||
20 | #include <asm/mpu.h> | ||
18 | 21 | ||
19 | #include "mm.h" | 22 | #include "mm.h" |
20 | 23 | ||
24 | #ifdef CONFIG_ARM_MPU | ||
25 | struct mpu_rgn_info mpu_rgn_info; | ||
26 | |||
27 | /* Region number */ | ||
28 | static void rgnr_write(u32 v) | ||
29 | { | ||
30 | asm("mcr p15, 0, %0, c6, c2, 0" : : "r" (v)); | ||
31 | } | ||
32 | |||
33 | /* Data-side / unified region attributes */ | ||
34 | |||
35 | /* Region access control register */ | ||
36 | static void dracr_write(u32 v) | ||
37 | { | ||
38 | asm("mcr p15, 0, %0, c6, c1, 4" : : "r" (v)); | ||
39 | } | ||
40 | |||
41 | /* Region size register */ | ||
42 | static void drsr_write(u32 v) | ||
43 | { | ||
44 | asm("mcr p15, 0, %0, c6, c1, 2" : : "r" (v)); | ||
45 | } | ||
46 | |||
47 | /* Region base address register */ | ||
48 | static void drbar_write(u32 v) | ||
49 | { | ||
50 | asm("mcr p15, 0, %0, c6, c1, 0" : : "r" (v)); | ||
51 | } | ||
52 | |||
53 | static u32 drbar_read(void) | ||
54 | { | ||
55 | u32 v; | ||
56 | asm("mrc p15, 0, %0, c6, c1, 0" : "=r" (v)); | ||
57 | return v; | ||
58 | } | ||
59 | /* Optional instruction-side region attributes */ | ||
60 | |||
61 | /* I-side Region access control register */ | ||
62 | static void iracr_write(u32 v) | ||
63 | { | ||
64 | asm("mcr p15, 0, %0, c6, c1, 5" : : "r" (v)); | ||
65 | } | ||
66 | |||
67 | /* I-side Region size register */ | ||
68 | static void irsr_write(u32 v) | ||
69 | { | ||
70 | asm("mcr p15, 0, %0, c6, c1, 3" : : "r" (v)); | ||
71 | } | ||
72 | |||
73 | /* I-side Region base address register */ | ||
74 | static void irbar_write(u32 v) | ||
75 | { | ||
76 | asm("mcr p15, 0, %0, c6, c1, 1" : : "r" (v)); | ||
77 | } | ||
78 | |||
79 | static unsigned long irbar_read(void) | ||
80 | { | ||
81 | unsigned long v; | ||
82 | asm("mrc p15, 0, %0, c6, c1, 1" : "=r" (v)); | ||
83 | return v; | ||
84 | } | ||
85 | |||
86 | /* MPU initialisation functions */ | ||
87 | void __init sanity_check_meminfo_mpu(void) | ||
88 | { | ||
89 | int i; | ||
90 | struct membank *bank = meminfo.bank; | ||
91 | phys_addr_t phys_offset = PHYS_OFFSET; | ||
92 | phys_addr_t aligned_region_size, specified_mem_size, rounded_mem_size; | ||
93 | |||
94 | /* Initially only use memory continuous from PHYS_OFFSET */ | ||
95 | if (bank_phys_start(&bank[0]) != phys_offset) | ||
96 | panic("First memory bank must be contiguous from PHYS_OFFSET"); | ||
97 | |||
98 | /* Banks have already been sorted by start address */ | ||
99 | for (i = 1; i < meminfo.nr_banks; i++) { | ||
100 | if (bank[i].start <= bank_phys_end(&bank[0]) && | ||
101 | bank_phys_end(&bank[i]) > bank_phys_end(&bank[0])) { | ||
102 | bank[0].size = bank_phys_end(&bank[i]) - bank[0].start; | ||
103 | } else { | ||
104 | pr_notice("Ignoring RAM after 0x%.8lx. " | ||
105 | "First non-contiguous (ignored) bank start: 0x%.8lx\n", | ||
106 | (unsigned long)bank_phys_end(&bank[0]), | ||
107 | (unsigned long)bank_phys_start(&bank[i])); | ||
108 | break; | ||
109 | } | ||
110 | } | ||
111 | /* All contiguous banks are now merged in to the first bank */ | ||
112 | meminfo.nr_banks = 1; | ||
113 | specified_mem_size = bank[0].size; | ||
114 | |||
115 | /* | ||
116 | * MPU has curious alignment requirements: Size must be power of 2, and | ||
117 | * region start must be aligned to the region size | ||
118 | */ | ||
119 | if (phys_offset != 0) | ||
120 | pr_info("PHYS_OFFSET != 0 => MPU Region size constrained by alignment requirements\n"); | ||
121 | |||
122 | /* | ||
123 | * Maximum aligned region might overflow phys_addr_t if phys_offset is | ||
124 | * 0. Hence we keep everything below 4G until we take the smaller of | ||
125 | * the aligned_region_size and rounded_mem_size, one of which is | ||
126 | * guaranteed to be smaller than the maximum physical address. | ||
127 | */ | ||
128 | aligned_region_size = (phys_offset - 1) ^ (phys_offset); | ||
129 | /* Find the max power-of-two sized region that fits inside our bank */ | ||
130 | rounded_mem_size = (1 << __fls(bank[0].size)) - 1; | ||
131 | |||
132 | /* The actual region size is the smaller of the two */ | ||
133 | aligned_region_size = aligned_region_size < rounded_mem_size | ||
134 | ? aligned_region_size + 1 | ||
135 | : rounded_mem_size + 1; | ||
136 | |||
137 | if (aligned_region_size != specified_mem_size) | ||
138 | pr_warn("Truncating memory from 0x%.8lx to 0x%.8lx (MPU region constraints)", | ||
139 | (unsigned long)specified_mem_size, | ||
140 | (unsigned long)aligned_region_size); | ||
141 | |||
142 | meminfo.bank[0].size = aligned_region_size; | ||
143 | pr_debug("MPU Region from 0x%.8lx size 0x%.8lx (end 0x%.8lx))\n", | ||
144 | (unsigned long)phys_offset, | ||
145 | (unsigned long)aligned_region_size, | ||
146 | (unsigned long)bank_phys_end(&bank[0])); | ||
147 | |||
148 | } | ||
149 | |||
150 | static int mpu_present(void) | ||
151 | { | ||
152 | return ((read_cpuid_ext(CPUID_EXT_MMFR0) & MMFR0_PMSA) == MMFR0_PMSAv7); | ||
153 | } | ||
154 | |||
155 | static int mpu_max_regions(void) | ||
156 | { | ||
157 | /* | ||
158 | * We don't support a different number of I/D side regions so if we | ||
159 | * have separate instruction and data memory maps then return | ||
160 | * whichever side has a smaller number of supported regions. | ||
161 | */ | ||
162 | u32 dregions, iregions, mpuir; | ||
163 | mpuir = read_cpuid(CPUID_MPUIR); | ||
164 | |||
165 | dregions = iregions = (mpuir & MPUIR_DREGION_SZMASK) >> MPUIR_DREGION; | ||
166 | |||
167 | /* Check for separate d-side and i-side memory maps */ | ||
168 | if (mpuir & MPUIR_nU) | ||
169 | iregions = (mpuir & MPUIR_IREGION_SZMASK) >> MPUIR_IREGION; | ||
170 | |||
171 | /* Use the smallest of the two maxima */ | ||
172 | return min(dregions, iregions); | ||
173 | } | ||
174 | |||
175 | static int mpu_iside_independent(void) | ||
176 | { | ||
177 | /* MPUIR.nU specifies whether there is *not* a unified memory map */ | ||
178 | return read_cpuid(CPUID_MPUIR) & MPUIR_nU; | ||
179 | } | ||
180 | |||
181 | static int mpu_min_region_order(void) | ||
182 | { | ||
183 | u32 drbar_result, irbar_result; | ||
184 | /* We've kept a region free for this probing */ | ||
185 | rgnr_write(MPU_PROBE_REGION); | ||
186 | isb(); | ||
187 | /* | ||
188 | * As per ARM ARM, write 0xFFFFFFFC to DRBAR to find the minimum | ||
189 | * region order | ||
190 | */ | ||
191 | drbar_write(0xFFFFFFFC); | ||
192 | drbar_result = irbar_result = drbar_read(); | ||
193 | drbar_write(0x0); | ||
194 | /* If the MPU is non-unified, we use the larger of the two minima*/ | ||
195 | if (mpu_iside_independent()) { | ||
196 | irbar_write(0xFFFFFFFC); | ||
197 | irbar_result = irbar_read(); | ||
198 | irbar_write(0x0); | ||
199 | } | ||
200 | isb(); /* Ensure that MPU region operations have completed */ | ||
201 | /* Return whichever result is larger */ | ||
202 | return __ffs(max(drbar_result, irbar_result)); | ||
203 | } | ||
204 | |||
205 | static int mpu_setup_region(unsigned int number, phys_addr_t start, | ||
206 | unsigned int size_order, unsigned int properties) | ||
207 | { | ||
208 | u32 size_data; | ||
209 | |||
210 | /* We kept a region free for probing resolution of MPU regions*/ | ||
211 | if (number > mpu_max_regions() || number == MPU_PROBE_REGION) | ||
212 | return -ENOENT; | ||
213 | |||
214 | if (size_order > 32) | ||
215 | return -ENOMEM; | ||
216 | |||
217 | if (size_order < mpu_min_region_order()) | ||
218 | return -ENOMEM; | ||
219 | |||
220 | /* Writing N to bits 5:1 (RSR_SZ) specifies region size 2^N+1 */ | ||
221 | size_data = ((size_order - 1) << MPU_RSR_SZ) | 1 << MPU_RSR_EN; | ||
222 | |||
223 | dsb(); /* Ensure all previous data accesses occur with old mappings */ | ||
224 | rgnr_write(number); | ||
225 | isb(); | ||
226 | drbar_write(start); | ||
227 | dracr_write(properties); | ||
228 | isb(); /* Propagate properties before enabling region */ | ||
229 | drsr_write(size_data); | ||
230 | |||
231 | /* Check for independent I-side registers */ | ||
232 | if (mpu_iside_independent()) { | ||
233 | irbar_write(start); | ||
234 | iracr_write(properties); | ||
235 | isb(); | ||
236 | irsr_write(size_data); | ||
237 | } | ||
238 | isb(); | ||
239 | |||
240 | /* Store region info (we treat i/d side the same, so only store d) */ | ||
241 | mpu_rgn_info.rgns[number].dracr = properties; | ||
242 | mpu_rgn_info.rgns[number].drbar = start; | ||
243 | mpu_rgn_info.rgns[number].drsr = size_data; | ||
244 | return 0; | ||
245 | } | ||
246 | |||
247 | /* | ||
248 | * Set up default MPU regions, doing nothing if there is no MPU | ||
249 | */ | ||
250 | void __init mpu_setup(void) | ||
251 | { | ||
252 | int region_err; | ||
253 | if (!mpu_present()) | ||
254 | return; | ||
255 | |||
256 | region_err = mpu_setup_region(MPU_RAM_REGION, PHYS_OFFSET, | ||
257 | ilog2(meminfo.bank[0].size), | ||
258 | MPU_AP_PL1RW_PL0RW | MPU_RGN_NORMAL); | ||
259 | if (region_err) { | ||
260 | panic("MPU region initialization failure! %d", region_err); | ||
261 | } else { | ||
262 | pr_info("Using ARMv7 PMSA Compliant MPU. " | ||
263 | "Region independence: %s, Max regions: %d\n", | ||
264 | mpu_iside_independent() ? "Yes" : "No", | ||
265 | mpu_max_regions()); | ||
266 | } | ||
267 | } | ||
268 | #else | ||
269 | static void sanity_check_meminfo_mpu(void) {} | ||
270 | static void __init mpu_setup(void) {} | ||
271 | #endif /* CONFIG_ARM_MPU */ | ||
272 | |||
21 | void __init arm_mm_memblock_reserve(void) | 273 | void __init arm_mm_memblock_reserve(void) |
22 | { | 274 | { |
275 | #ifndef CONFIG_CPU_V7M | ||
23 | /* | 276 | /* |
24 | * Register the exception vector page. | 277 | * Register the exception vector page. |
25 | * some architectures which the DRAM is the exception vector to trap, | 278 | * some architectures which the DRAM is the exception vector to trap, |
26 | * alloc_page breaks with error, although it is not NULL, but "0." | 279 | * alloc_page breaks with error, although it is not NULL, but "0." |
27 | */ | 280 | */ |
28 | memblock_reserve(CONFIG_VECTORS_BASE, PAGE_SIZE); | 281 | memblock_reserve(CONFIG_VECTORS_BASE, PAGE_SIZE); |
282 | #else /* ifndef CONFIG_CPU_V7M */ | ||
283 | /* | ||
284 | * There is no dedicated vector page on V7-M. So nothing needs to be | ||
285 | * reserved here. | ||
286 | */ | ||
287 | #endif | ||
29 | } | 288 | } |
30 | 289 | ||
31 | void __init sanity_check_meminfo(void) | 290 | void __init sanity_check_meminfo(void) |
32 | { | 291 | { |
33 | phys_addr_t end = bank_phys_end(&meminfo.bank[meminfo.nr_banks - 1]); | 292 | phys_addr_t end; |
293 | sanity_check_meminfo_mpu(); | ||
294 | end = bank_phys_end(&meminfo.bank[meminfo.nr_banks - 1]); | ||
34 | high_memory = __va(end - 1) + 1; | 295 | high_memory = __va(end - 1) + 1; |
35 | } | 296 | } |
36 | 297 | ||
@@ -41,6 +302,7 @@ void __init sanity_check_meminfo(void) | |||
41 | void __init paging_init(struct machine_desc *mdesc) | 302 | void __init paging_init(struct machine_desc *mdesc) |
42 | { | 303 | { |
43 | early_trap_init((void *)CONFIG_VECTORS_BASE); | 304 | early_trap_init((void *)CONFIG_VECTORS_BASE); |
305 | mpu_setup(); | ||
44 | bootmem_init(); | 306 | bootmem_init(); |
45 | } | 307 | } |
46 | 308 | ||