diff options
Diffstat (limited to 'arch/avr32/mm/init.c')
-rw-r--r-- | arch/avr32/mm/init.c | 480 |
1 files changed, 480 insertions, 0 deletions
diff --git a/arch/avr32/mm/init.c b/arch/avr32/mm/init.c new file mode 100644 index 000000000000..3e6c41039808 --- /dev/null +++ b/arch/avr32/mm/init.c | |||
@@ -0,0 +1,480 @@ | |||
1 | /* | ||
2 | * Copyright (C) 2004-2006 Atmel Corporation | ||
3 | * | ||
4 | * This program is free software; you can redistribute it and/or modify | ||
5 | * it under the terms of the GNU General Public License version 2 as | ||
6 | * published by the Free Software Foundation. | ||
7 | */ | ||
8 | |||
9 | #include <linux/kernel.h> | ||
10 | #include <linux/mm.h> | ||
11 | #include <linux/swap.h> | ||
12 | #include <linux/init.h> | ||
13 | #include <linux/initrd.h> | ||
14 | #include <linux/mmzone.h> | ||
15 | #include <linux/bootmem.h> | ||
16 | #include <linux/pagemap.h> | ||
17 | #include <linux/pfn.h> | ||
18 | #include <linux/nodemask.h> | ||
19 | |||
20 | #include <asm/page.h> | ||
21 | #include <asm/mmu_context.h> | ||
22 | #include <asm/tlb.h> | ||
23 | #include <asm/io.h> | ||
24 | #include <asm/dma.h> | ||
25 | #include <asm/setup.h> | ||
26 | #include <asm/sections.h> | ||
27 | |||
28 | DEFINE_PER_CPU(struct mmu_gather, mmu_gathers); | ||
29 | |||
30 | pgd_t swapper_pg_dir[PTRS_PER_PGD]; | ||
31 | |||
32 | struct page *empty_zero_page; | ||
33 | |||
34 | /* | ||
35 | * Cache of MMU context last used. | ||
36 | */ | ||
37 | unsigned long mmu_context_cache = NO_CONTEXT; | ||
38 | |||
39 | #define START_PFN (NODE_DATA(0)->bdata->node_boot_start >> PAGE_SHIFT) | ||
40 | #define MAX_LOW_PFN (NODE_DATA(0)->bdata->node_low_pfn) | ||
41 | |||
42 | void show_mem(void) | ||
43 | { | ||
44 | int total = 0, reserved = 0, cached = 0; | ||
45 | int slab = 0, free = 0, shared = 0; | ||
46 | pg_data_t *pgdat; | ||
47 | |||
48 | printk("Mem-info:\n"); | ||
49 | show_free_areas(); | ||
50 | |||
51 | for_each_online_pgdat(pgdat) { | ||
52 | struct page *page, *end; | ||
53 | |||
54 | page = pgdat->node_mem_map; | ||
55 | end = page + pgdat->node_spanned_pages; | ||
56 | |||
57 | do { | ||
58 | total++; | ||
59 | if (PageReserved(page)) | ||
60 | reserved++; | ||
61 | else if (PageSwapCache(page)) | ||
62 | cached++; | ||
63 | else if (PageSlab(page)) | ||
64 | slab++; | ||
65 | else if (!page_count(page)) | ||
66 | free++; | ||
67 | else | ||
68 | shared += page_count(page) - 1; | ||
69 | page++; | ||
70 | } while (page < end); | ||
71 | } | ||
72 | |||
73 | printk ("%d pages of RAM\n", total); | ||
74 | printk ("%d free pages\n", free); | ||
75 | printk ("%d reserved pages\n", reserved); | ||
76 | printk ("%d slab pages\n", slab); | ||
77 | printk ("%d pages shared\n", shared); | ||
78 | printk ("%d pages swap cached\n", cached); | ||
79 | } | ||
80 | |||
81 | static void __init print_memory_map(const char *what, | ||
82 | struct tag_mem_range *mem) | ||
83 | { | ||
84 | printk ("%s:\n", what); | ||
85 | for (; mem; mem = mem->next) { | ||
86 | printk (" %08lx - %08lx\n", | ||
87 | (unsigned long)mem->addr, | ||
88 | (unsigned long)(mem->addr + mem->size)); | ||
89 | } | ||
90 | } | ||
91 | |||
92 | #define MAX_LOWMEM HIGHMEM_START | ||
93 | #define MAX_LOWMEM_PFN PFN_DOWN(MAX_LOWMEM) | ||
94 | |||
95 | /* | ||
96 | * Sort a list of memory regions in-place by ascending address. | ||
97 | * | ||
98 | * We're using bubble sort because we only have singly linked lists | ||
99 | * with few elements. | ||
100 | */ | ||
101 | static void __init sort_mem_list(struct tag_mem_range **pmem) | ||
102 | { | ||
103 | int done; | ||
104 | struct tag_mem_range **a, **b; | ||
105 | |||
106 | if (!*pmem) | ||
107 | return; | ||
108 | |||
109 | do { | ||
110 | done = 1; | ||
111 | a = pmem, b = &(*pmem)->next; | ||
112 | while (*b) { | ||
113 | if ((*a)->addr > (*b)->addr) { | ||
114 | struct tag_mem_range *tmp; | ||
115 | tmp = (*b)->next; | ||
116 | (*b)->next = *a; | ||
117 | *a = *b; | ||
118 | *b = tmp; | ||
119 | done = 0; | ||
120 | } | ||
121 | a = &(*a)->next; | ||
122 | b = &(*a)->next; | ||
123 | } | ||
124 | } while (!done); | ||
125 | } | ||
126 | |||
127 | /* | ||
128 | * Find a free memory region large enough for storing the | ||
129 | * bootmem bitmap. | ||
130 | */ | ||
131 | static unsigned long __init | ||
132 | find_bootmap_pfn(const struct tag_mem_range *mem) | ||
133 | { | ||
134 | unsigned long bootmap_pages, bootmap_len; | ||
135 | unsigned long node_pages = PFN_UP(mem->size); | ||
136 | unsigned long bootmap_addr = mem->addr; | ||
137 | struct tag_mem_range *reserved = mem_reserved; | ||
138 | struct tag_mem_range *ramdisk = mem_ramdisk; | ||
139 | unsigned long kern_start = virt_to_phys(_stext); | ||
140 | unsigned long kern_end = virt_to_phys(_end); | ||
141 | |||
142 | bootmap_pages = bootmem_bootmap_pages(node_pages); | ||
143 | bootmap_len = bootmap_pages << PAGE_SHIFT; | ||
144 | |||
145 | /* | ||
146 | * Find a large enough region without reserved pages for | ||
147 | * storing the bootmem bitmap. We can take advantage of the | ||
148 | * fact that all lists have been sorted. | ||
149 | * | ||
150 | * We have to check explicitly reserved regions as well as the | ||
151 | * kernel image and any RAMDISK images... | ||
152 | * | ||
153 | * Oh, and we have to make sure we don't overwrite the taglist | ||
154 | * since we're going to use it until the bootmem allocator is | ||
155 | * fully up and running. | ||
156 | */ | ||
157 | while (1) { | ||
158 | if ((bootmap_addr < kern_end) && | ||
159 | ((bootmap_addr + bootmap_len) > kern_start)) | ||
160 | bootmap_addr = kern_end; | ||
161 | |||
162 | while (reserved && | ||
163 | (bootmap_addr >= (reserved->addr + reserved->size))) | ||
164 | reserved = reserved->next; | ||
165 | |||
166 | if (reserved && | ||
167 | ((bootmap_addr + bootmap_len) >= reserved->addr)) { | ||
168 | bootmap_addr = reserved->addr + reserved->size; | ||
169 | continue; | ||
170 | } | ||
171 | |||
172 | while (ramdisk && | ||
173 | (bootmap_addr >= (ramdisk->addr + ramdisk->size))) | ||
174 | ramdisk = ramdisk->next; | ||
175 | |||
176 | if (!ramdisk || | ||
177 | ((bootmap_addr + bootmap_len) < ramdisk->addr)) | ||
178 | break; | ||
179 | |||
180 | bootmap_addr = ramdisk->addr + ramdisk->size; | ||
181 | } | ||
182 | |||
183 | if ((PFN_UP(bootmap_addr) + bootmap_len) >= (mem->addr + mem->size)) | ||
184 | return ~0UL; | ||
185 | |||
186 | return PFN_UP(bootmap_addr); | ||
187 | } | ||
188 | |||
189 | void __init setup_bootmem(void) | ||
190 | { | ||
191 | unsigned bootmap_size; | ||
192 | unsigned long first_pfn, bootmap_pfn, pages; | ||
193 | unsigned long max_pfn, max_low_pfn; | ||
194 | unsigned long kern_start = virt_to_phys(_stext); | ||
195 | unsigned long kern_end = virt_to_phys(_end); | ||
196 | unsigned node = 0; | ||
197 | struct tag_mem_range *bank, *res; | ||
198 | |||
199 | sort_mem_list(&mem_phys); | ||
200 | sort_mem_list(&mem_reserved); | ||
201 | |||
202 | print_memory_map("Physical memory", mem_phys); | ||
203 | print_memory_map("Reserved memory", mem_reserved); | ||
204 | |||
205 | nodes_clear(node_online_map); | ||
206 | |||
207 | if (mem_ramdisk) { | ||
208 | #ifdef CONFIG_BLK_DEV_INITRD | ||
209 | initrd_start = __va(mem_ramdisk->addr); | ||
210 | initrd_end = initrd_start + mem_ramdisk->size; | ||
211 | |||
212 | print_memory_map("RAMDISK images", mem_ramdisk); | ||
213 | if (mem_ramdisk->next) | ||
214 | printk(KERN_WARNING | ||
215 | "Warning: Only the first RAMDISK image " | ||
216 | "will be used\n"); | ||
217 | sort_mem_list(&mem_ramdisk); | ||
218 | #else | ||
219 | printk(KERN_WARNING "RAM disk image present, but " | ||
220 | "no initrd support in kernel!\n"); | ||
221 | #endif | ||
222 | } | ||
223 | |||
224 | if (mem_phys->next) | ||
225 | printk(KERN_WARNING "Only using first memory bank\n"); | ||
226 | |||
227 | for (bank = mem_phys; bank; bank = NULL) { | ||
228 | first_pfn = PFN_UP(bank->addr); | ||
229 | max_low_pfn = max_pfn = PFN_DOWN(bank->addr + bank->size); | ||
230 | bootmap_pfn = find_bootmap_pfn(bank); | ||
231 | if (bootmap_pfn > max_pfn) | ||
232 | panic("No space for bootmem bitmap!\n"); | ||
233 | |||
234 | if (max_low_pfn > MAX_LOWMEM_PFN) { | ||
235 | max_low_pfn = MAX_LOWMEM_PFN; | ||
236 | #ifndef CONFIG_HIGHMEM | ||
237 | /* | ||
238 | * Lowmem is memory that can be addressed | ||
239 | * directly through P1/P2 | ||
240 | */ | ||
241 | printk(KERN_WARNING | ||
242 | "Node %u: Only %ld MiB of memory will be used.\n", | ||
243 | node, MAX_LOWMEM >> 20); | ||
244 | printk(KERN_WARNING "Use a HIGHMEM enabled kernel.\n"); | ||
245 | #else | ||
246 | #error HIGHMEM is not supported by AVR32 yet | ||
247 | #endif | ||
248 | } | ||
249 | |||
250 | /* Initialize the boot-time allocator with low memory only. */ | ||
251 | bootmap_size = init_bootmem_node(NODE_DATA(node), bootmap_pfn, | ||
252 | first_pfn, max_low_pfn); | ||
253 | |||
254 | printk("Node %u: bdata = %p, bdata->node_bootmem_map = %p\n", | ||
255 | node, NODE_DATA(node)->bdata, | ||
256 | NODE_DATA(node)->bdata->node_bootmem_map); | ||
257 | |||
258 | /* | ||
259 | * Register fully available RAM pages with the bootmem | ||
260 | * allocator. | ||
261 | */ | ||
262 | pages = max_low_pfn - first_pfn; | ||
263 | free_bootmem_node (NODE_DATA(node), PFN_PHYS(first_pfn), | ||
264 | PFN_PHYS(pages)); | ||
265 | |||
266 | /* | ||
267 | * Reserve space for the kernel image (if present in | ||
268 | * this node)... | ||
269 | */ | ||
270 | if ((kern_start >= PFN_PHYS(first_pfn)) && | ||
271 | (kern_start < PFN_PHYS(max_pfn))) { | ||
272 | printk("Node %u: Kernel image %08lx - %08lx\n", | ||
273 | node, kern_start, kern_end); | ||
274 | reserve_bootmem_node(NODE_DATA(node), kern_start, | ||
275 | kern_end - kern_start); | ||
276 | } | ||
277 | |||
278 | /* ...the bootmem bitmap... */ | ||
279 | reserve_bootmem_node(NODE_DATA(node), | ||
280 | PFN_PHYS(bootmap_pfn), | ||
281 | bootmap_size); | ||
282 | |||
283 | /* ...any RAMDISK images... */ | ||
284 | for (res = mem_ramdisk; res; res = res->next) { | ||
285 | if (res->addr > PFN_PHYS(max_pfn)) | ||
286 | break; | ||
287 | |||
288 | if (res->addr >= PFN_PHYS(first_pfn)) { | ||
289 | printk("Node %u: RAMDISK %08lx - %08lx\n", | ||
290 | node, | ||
291 | (unsigned long)res->addr, | ||
292 | (unsigned long)(res->addr + res->size)); | ||
293 | reserve_bootmem_node(NODE_DATA(node), | ||
294 | res->addr, res->size); | ||
295 | } | ||
296 | } | ||
297 | |||
298 | /* ...and any other reserved regions. */ | ||
299 | for (res = mem_reserved; res; res = res->next) { | ||
300 | if (res->addr > PFN_PHYS(max_pfn)) | ||
301 | break; | ||
302 | |||
303 | if (res->addr >= PFN_PHYS(first_pfn)) { | ||
304 | printk("Node %u: Reserved %08lx - %08lx\n", | ||
305 | node, | ||
306 | (unsigned long)res->addr, | ||
307 | (unsigned long)(res->addr + res->size)); | ||
308 | reserve_bootmem_node(NODE_DATA(node), | ||
309 | res->addr, res->size); | ||
310 | } | ||
311 | } | ||
312 | |||
313 | node_set_online(node); | ||
314 | } | ||
315 | } | ||
316 | |||
317 | /* | ||
318 | * paging_init() sets up the page tables | ||
319 | * | ||
320 | * This routine also unmaps the page at virtual kernel address 0, so | ||
321 | * that we can trap those pesky NULL-reference errors in the kernel. | ||
322 | */ | ||
323 | void __init paging_init(void) | ||
324 | { | ||
325 | extern unsigned long _evba; | ||
326 | void *zero_page; | ||
327 | int nid; | ||
328 | |||
329 | /* | ||
330 | * Make sure we can handle exceptions before enabling | ||
331 | * paging. Not that we should ever _get_ any exceptions this | ||
332 | * early, but you never know... | ||
333 | */ | ||
334 | printk("Exception vectors start at %p\n", &_evba); | ||
335 | sysreg_write(EVBA, (unsigned long)&_evba); | ||
336 | |||
337 | /* | ||
338 | * Since we are ready to handle exceptions now, we should let | ||
339 | * the CPU generate them... | ||
340 | */ | ||
341 | __asm__ __volatile__ ("csrf %0" : : "i"(SR_EM_BIT)); | ||
342 | |||
343 | /* | ||
344 | * Allocate the zero page. The allocator will panic if it | ||
345 | * can't satisfy the request, so no need to check. | ||
346 | */ | ||
347 | zero_page = alloc_bootmem_low_pages_node(NODE_DATA(0), | ||
348 | PAGE_SIZE); | ||
349 | |||
350 | { | ||
351 | pgd_t *pg_dir; | ||
352 | int i; | ||
353 | |||
354 | pg_dir = swapper_pg_dir; | ||
355 | sysreg_write(PTBR, (unsigned long)pg_dir); | ||
356 | |||
357 | for (i = 0; i < PTRS_PER_PGD; i++) | ||
358 | pgd_val(pg_dir[i]) = 0; | ||
359 | |||
360 | enable_mmu(); | ||
361 | printk ("CPU: Paging enabled\n"); | ||
362 | } | ||
363 | |||
364 | for_each_online_node(nid) { | ||
365 | pg_data_t *pgdat = NODE_DATA(nid); | ||
366 | unsigned long zones_size[MAX_NR_ZONES]; | ||
367 | unsigned long low, start_pfn; | ||
368 | |||
369 | start_pfn = pgdat->bdata->node_boot_start; | ||
370 | start_pfn >>= PAGE_SHIFT; | ||
371 | low = pgdat->bdata->node_low_pfn; | ||
372 | |||
373 | memset(zones_size, 0, sizeof(zones_size)); | ||
374 | zones_size[ZONE_NORMAL] = low - start_pfn; | ||
375 | |||
376 | printk("Node %u: start_pfn = 0x%lx, low = 0x%lx\n", | ||
377 | nid, start_pfn, low); | ||
378 | |||
379 | free_area_init_node(nid, pgdat, zones_size, start_pfn, NULL); | ||
380 | |||
381 | printk("Node %u: mem_map starts at %p\n", | ||
382 | pgdat->node_id, pgdat->node_mem_map); | ||
383 | } | ||
384 | |||
385 | mem_map = NODE_DATA(0)->node_mem_map; | ||
386 | |||
387 | memset(zero_page, 0, PAGE_SIZE); | ||
388 | empty_zero_page = virt_to_page(zero_page); | ||
389 | flush_dcache_page(empty_zero_page); | ||
390 | } | ||
391 | |||
392 | void __init mem_init(void) | ||
393 | { | ||
394 | int codesize, reservedpages, datasize, initsize; | ||
395 | int nid, i; | ||
396 | |||
397 | reservedpages = 0; | ||
398 | high_memory = NULL; | ||
399 | |||
400 | /* this will put all low memory onto the freelists */ | ||
401 | for_each_online_node(nid) { | ||
402 | pg_data_t *pgdat = NODE_DATA(nid); | ||
403 | unsigned long node_pages = 0; | ||
404 | void *node_high_memory; | ||
405 | |||
406 | num_physpages += pgdat->node_present_pages; | ||
407 | |||
408 | if (pgdat->node_spanned_pages != 0) | ||
409 | node_pages = free_all_bootmem_node(pgdat); | ||
410 | |||
411 | totalram_pages += node_pages; | ||
412 | |||
413 | for (i = 0; i < node_pages; i++) | ||
414 | if (PageReserved(pgdat->node_mem_map + i)) | ||
415 | reservedpages++; | ||
416 | |||
417 | node_high_memory = (void *)((pgdat->node_start_pfn | ||
418 | + pgdat->node_spanned_pages) | ||
419 | << PAGE_SHIFT); | ||
420 | if (node_high_memory > high_memory) | ||
421 | high_memory = node_high_memory; | ||
422 | } | ||
423 | |||
424 | max_mapnr = MAP_NR(high_memory); | ||
425 | |||
426 | codesize = (unsigned long)_etext - (unsigned long)_text; | ||
427 | datasize = (unsigned long)_edata - (unsigned long)_data; | ||
428 | initsize = (unsigned long)__init_end - (unsigned long)__init_begin; | ||
429 | |||
430 | printk ("Memory: %luk/%luk available (%dk kernel code, " | ||
431 | "%dk reserved, %dk data, %dk init)\n", | ||
432 | (unsigned long)nr_free_pages() << (PAGE_SHIFT - 10), | ||
433 | totalram_pages << (PAGE_SHIFT - 10), | ||
434 | codesize >> 10, | ||
435 | reservedpages << (PAGE_SHIFT - 10), | ||
436 | datasize >> 10, | ||
437 | initsize >> 10); | ||
438 | } | ||
439 | |||
440 | static inline void free_area(unsigned long addr, unsigned long end, char *s) | ||
441 | { | ||
442 | unsigned int size = (end - addr) >> 10; | ||
443 | |||
444 | for (; addr < end; addr += PAGE_SIZE) { | ||
445 | struct page *page = virt_to_page(addr); | ||
446 | ClearPageReserved(page); | ||
447 | init_page_count(page); | ||
448 | free_page(addr); | ||
449 | totalram_pages++; | ||
450 | } | ||
451 | |||
452 | if (size && s) | ||
453 | printk(KERN_INFO "Freeing %s memory: %dK (%lx - %lx)\n", | ||
454 | s, size, end - (size << 10), end); | ||
455 | } | ||
456 | |||
457 | void free_initmem(void) | ||
458 | { | ||
459 | free_area((unsigned long)__init_begin, (unsigned long)__init_end, | ||
460 | "init"); | ||
461 | } | ||
462 | |||
463 | #ifdef CONFIG_BLK_DEV_INITRD | ||
464 | |||
465 | static int keep_initrd; | ||
466 | |||
467 | void free_initrd_mem(unsigned long start, unsigned long end) | ||
468 | { | ||
469 | if (!keep_initrd) | ||
470 | free_area(start, end, "initrd"); | ||
471 | } | ||
472 | |||
473 | static int __init keepinitrd_setup(char *__unused) | ||
474 | { | ||
475 | keep_initrd = 1; | ||
476 | return 1; | ||
477 | } | ||
478 | |||
479 | __setup("keepinitrd", keepinitrd_setup); | ||
480 | #endif | ||