Merge tag 'sunxi-dt-for-3.17-2' of git://git.kernel.org/pub/scm/linux/kernel/git...
[cascardo/linux.git] / arch / xtensa / mm / init.c
1 /*
2  * arch/xtensa/mm/init.c
3  *
4  * Derived from MIPS, PPC.
5  *
6  * This file is subject to the terms and conditions of the GNU General Public
7  * License.  See the file "COPYING" in the main directory of this archive
8  * for more details.
9  *
10  * Copyright (C) 2001 - 2005 Tensilica Inc.
11  * Copyright (C) 2014 Cadence Design Systems Inc.
12  *
13  * Chris Zankel <chris@zankel.net>
14  * Joe Taylor   <joe@tensilica.com, joetylr@yahoo.com>
15  * Marc Gauthier
16  * Kevin Chea
17  */
18
19 #include <linux/kernel.h>
20 #include <linux/errno.h>
21 #include <linux/bootmem.h>
22 #include <linux/gfp.h>
23 #include <linux/highmem.h>
24 #include <linux/swap.h>
25 #include <linux/mman.h>
26 #include <linux/nodemask.h>
27 #include <linux/mm.h>
28
29 #include <asm/bootparam.h>
30 #include <asm/page.h>
31 #include <asm/sections.h>
32 #include <asm/sysmem.h>
33
34 struct sysmem_info sysmem __initdata;
35
36 static void __init sysmem_dump(void)
37 {
38         unsigned i;
39
40         pr_debug("Sysmem:\n");
41         for (i = 0; i < sysmem.nr_banks; ++i)
42                 pr_debug("  0x%08lx - 0x%08lx (%ldK)\n",
43                          sysmem.bank[i].start, sysmem.bank[i].end,
44                          (sysmem.bank[i].end - sysmem.bank[i].start) >> 10);
45 }
46
47 /*
48  * Find bank with maximal .start such that bank.start <= start
49  */
50 static inline struct meminfo * __init find_bank(unsigned long start)
51 {
52         unsigned i;
53         struct meminfo *it = NULL;
54
55         for (i = 0; i < sysmem.nr_banks; ++i)
56                 if (sysmem.bank[i].start <= start)
57                         it = sysmem.bank + i;
58                 else
59                         break;
60         return it;
61 }
62
63 /*
64  * Move all memory banks starting at 'from' to a new place at 'to',
65  * adjust nr_banks accordingly.
66  * Both 'from' and 'to' must be inside the sysmem.bank.
67  *
68  * Returns: 0 (success), -ENOMEM (not enough space in the sysmem.bank).
69  */
70 static int __init move_banks(struct meminfo *to, struct meminfo *from)
71 {
72         unsigned n = sysmem.nr_banks - (from - sysmem.bank);
73
74         if (to > from && to - from + sysmem.nr_banks > SYSMEM_BANKS_MAX)
75                 return -ENOMEM;
76         if (to != from)
77                 memmove(to, from, n * sizeof(struct meminfo));
78         sysmem.nr_banks += to - from;
79         return 0;
80 }
81
82 /*
83  * Add new bank to sysmem. Resulting sysmem is the union of bytes of the
84  * original sysmem and the new bank.
85  *
86  * Returns: 0 (success), < 0 (error)
87  */
88 int __init add_sysmem_bank(unsigned long start, unsigned long end)
89 {
90         unsigned i;
91         struct meminfo *it = NULL;
92         unsigned long sz;
93         unsigned long bank_sz = 0;
94
95         if (start == end ||
96             (start < end) != (PAGE_ALIGN(start) < (end & PAGE_MASK))) {
97                 pr_warn("Ignoring small memory bank 0x%08lx size: %ld bytes\n",
98                         start, end - start);
99                 return -EINVAL;
100         }
101
102         start = PAGE_ALIGN(start);
103         end &= PAGE_MASK;
104         sz = end - start;
105
106         it = find_bank(start);
107
108         if (it)
109                 bank_sz = it->end - it->start;
110
111         if (it && bank_sz >= start - it->start) {
112                 if (end - it->start > bank_sz)
113                         it->end = end;
114                 else
115                         return 0;
116         } else {
117                 if (!it)
118                         it = sysmem.bank;
119                 else
120                         ++it;
121
122                 if (it - sysmem.bank < sysmem.nr_banks &&
123                     it->start - start <= sz) {
124                         it->start = start;
125                         if (it->end - it->start < sz)
126                                 it->end = end;
127                         else
128                                 return 0;
129                 } else {
130                         if (move_banks(it + 1, it) < 0) {
131                                 pr_warn("Ignoring memory bank 0x%08lx size %ld bytes\n",
132                                         start, end - start);
133                                 return -EINVAL;
134                         }
135                         it->start = start;
136                         it->end = end;
137                         return 0;
138                 }
139         }
140         sz = it->end - it->start;
141         for (i = it + 1 - sysmem.bank; i < sysmem.nr_banks; ++i)
142                 if (sysmem.bank[i].start - it->start <= sz) {
143                         if (sz < sysmem.bank[i].end - it->start)
144                                 it->end = sysmem.bank[i].end;
145                 } else {
146                         break;
147                 }
148
149         move_banks(it + 1, sysmem.bank + i);
150         return 0;
151 }
152
153 /*
154  * mem_reserve(start, end, must_exist)
155  *
156  * Reserve some memory from the memory pool.
157  * If must_exist is set and a part of the region being reserved does not exist
158  * memory map is not altered.
159  *
160  * Parameters:
161  *  start       Start of region,
162  *  end         End of region,
163  *  must_exist  Must exist in memory pool.
164  *
165  * Returns:
166  *  0 (success)
167  *  < 0 (error)
168  */
169
170 int __init mem_reserve(unsigned long start, unsigned long end, int must_exist)
171 {
172         struct meminfo *it;
173         struct meminfo *rm = NULL;
174         unsigned long sz;
175         unsigned long bank_sz = 0;
176
177         start = start & PAGE_MASK;
178         end = PAGE_ALIGN(end);
179         sz = end - start;
180         if (!sz)
181                 return -EINVAL;
182
183         it = find_bank(start);
184
185         if (it)
186                 bank_sz = it->end - it->start;
187
188         if ((!it || end - it->start > bank_sz) && must_exist) {
189                 pr_warn("mem_reserve: [0x%0lx, 0x%0lx) not in any region!\n",
190                         start, end);
191                 return -EINVAL;
192         }
193
194         if (it && start - it->start <= bank_sz) {
195                 if (start == it->start) {
196                         if (end - it->start < bank_sz) {
197                                 it->start = end;
198                                 return 0;
199                         } else {
200                                 rm = it;
201                         }
202                 } else {
203                         it->end = start;
204                         if (end - it->start < bank_sz)
205                                 return add_sysmem_bank(end,
206                                                        it->start + bank_sz);
207                         ++it;
208                 }
209         }
210
211         if (!it)
212                 it = sysmem.bank;
213
214         for (; it < sysmem.bank + sysmem.nr_banks; ++it) {
215                 if (it->end - start <= sz) {
216                         if (!rm)
217                                 rm = it;
218                 } else {
219                         if (it->start - start < sz)
220                                 it->start = end;
221                         break;
222                 }
223         }
224
225         if (rm)
226                 move_banks(rm, it);
227
228         return 0;
229 }
230
231
232 /*
233  * Initialize the bootmem system and give it all low memory we have available.
234  */
235
236 void __init bootmem_init(void)
237 {
238         unsigned long pfn;
239         unsigned long bootmap_start, bootmap_size;
240         int i;
241
242         sysmem_dump();
243         max_low_pfn = max_pfn = 0;
244         min_low_pfn = ~0;
245
246         for (i=0; i < sysmem.nr_banks; i++) {
247                 pfn = PAGE_ALIGN(sysmem.bank[i].start) >> PAGE_SHIFT;
248                 if (pfn < min_low_pfn)
249                         min_low_pfn = pfn;
250                 pfn = PAGE_ALIGN(sysmem.bank[i].end - 1) >> PAGE_SHIFT;
251                 if (pfn > max_pfn)
252                         max_pfn = pfn;
253         }
254
255         if (min_low_pfn > max_pfn)
256                 panic("No memory found!\n");
257
258         max_low_pfn = max_pfn < MAX_MEM_PFN >> PAGE_SHIFT ?
259                 max_pfn : MAX_MEM_PFN >> PAGE_SHIFT;
260
261         /* Find an area to use for the bootmem bitmap. */
262
263         bootmap_size = bootmem_bootmap_pages(max_low_pfn - min_low_pfn);
264         bootmap_size <<= PAGE_SHIFT;
265         bootmap_start = ~0;
266
267         for (i=0; i<sysmem.nr_banks; i++)
268                 if (sysmem.bank[i].end - sysmem.bank[i].start >= bootmap_size) {
269                         bootmap_start = sysmem.bank[i].start;
270                         break;
271                 }
272
273         if (bootmap_start == ~0UL)
274                 panic("Cannot find %ld bytes for bootmap\n", bootmap_size);
275
276         /* Reserve the bootmem bitmap area */
277
278         mem_reserve(bootmap_start, bootmap_start + bootmap_size, 1);
279         bootmap_size = init_bootmem_node(NODE_DATA(0),
280                                          bootmap_start >> PAGE_SHIFT,
281                                          min_low_pfn,
282                                          max_low_pfn);
283
284         /* Add all remaining memory pieces into the bootmem map */
285
286         for (i = 0; i < sysmem.nr_banks; i++) {
287                 if (sysmem.bank[i].start >> PAGE_SHIFT < max_low_pfn) {
288                         unsigned long end = min(max_low_pfn << PAGE_SHIFT,
289                                                 sysmem.bank[i].end);
290                         free_bootmem(sysmem.bank[i].start,
291                                      end - sysmem.bank[i].start);
292                 }
293         }
294
295 }
296
297
298 void __init zones_init(void)
299 {
300         /* All pages are DMA-able, so we put them all in the DMA zone. */
301         unsigned long zones_size[MAX_NR_ZONES] = {
302                 [ZONE_DMA] = max_low_pfn - ARCH_PFN_OFFSET,
303 #ifdef CONFIG_HIGHMEM
304                 [ZONE_HIGHMEM] = max_pfn - max_low_pfn,
305 #endif
306         };
307         free_area_init_node(0, zones_size, ARCH_PFN_OFFSET, NULL);
308 }
309
310 /*
311  * Initialize memory pages.
312  */
313
314 void __init mem_init(void)
315 {
316 #ifdef CONFIG_HIGHMEM
317         unsigned long tmp;
318
319         reset_all_zones_managed_pages();
320         for (tmp = max_low_pfn; tmp < max_pfn; tmp++)
321                 free_highmem_page(pfn_to_page(tmp));
322 #endif
323
324         max_mapnr = max_pfn - ARCH_PFN_OFFSET;
325         high_memory = (void *)__va(max_low_pfn << PAGE_SHIFT);
326
327         free_all_bootmem();
328
329         mem_init_print_info(NULL);
330         pr_info("virtual kernel memory layout:\n"
331 #ifdef CONFIG_HIGHMEM
332                 "    pkmap   : 0x%08lx - 0x%08lx  (%5lu kB)\n"
333                 "    fixmap  : 0x%08lx - 0x%08lx  (%5lu kB)\n"
334 #endif
335                 "    vmalloc : 0x%08x - 0x%08x  (%5u MB)\n"
336                 "    lowmem  : 0x%08x - 0x%08lx  (%5lu MB)\n",
337 #ifdef CONFIG_HIGHMEM
338                 PKMAP_BASE, PKMAP_BASE + LAST_PKMAP * PAGE_SIZE,
339                 (LAST_PKMAP*PAGE_SIZE) >> 10,
340                 FIXADDR_START, FIXADDR_TOP,
341                 (FIXADDR_TOP - FIXADDR_START) >> 10,
342 #endif
343                 VMALLOC_START, VMALLOC_END,
344                 (VMALLOC_END - VMALLOC_START) >> 20,
345                 PAGE_OFFSET, PAGE_OFFSET +
346                 (max_low_pfn - min_low_pfn) * PAGE_SIZE,
347                 ((max_low_pfn - min_low_pfn) * PAGE_SIZE) >> 20);
348 }
349
350 #ifdef CONFIG_BLK_DEV_INITRD
351 extern int initrd_is_mapped;
352
353 void free_initrd_mem(unsigned long start, unsigned long end)
354 {
355         if (initrd_is_mapped)
356                 free_reserved_area((void *)start, (void *)end, -1, "initrd");
357 }
358 #endif
359
360 void free_initmem(void)
361 {
362         free_initmem_default(-1);
363 }
364
365 static void __init parse_memmap_one(char *p)
366 {
367         char *oldp;
368         unsigned long start_at, mem_size;
369
370         if (!p)
371                 return;
372
373         oldp = p;
374         mem_size = memparse(p, &p);
375         if (p == oldp)
376                 return;
377
378         switch (*p) {
379         case '@':
380                 start_at = memparse(p + 1, &p);
381                 add_sysmem_bank(start_at, start_at + mem_size);
382                 break;
383
384         case '$':
385                 start_at = memparse(p + 1, &p);
386                 mem_reserve(start_at, start_at + mem_size, 0);
387                 break;
388
389         case 0:
390                 mem_reserve(mem_size, 0, 0);
391                 break;
392
393         default:
394                 pr_warn("Unrecognized memmap syntax: %s\n", p);
395                 break;
396         }
397 }
398
399 static int __init parse_memmap_opt(char *str)
400 {
401         while (str) {
402                 char *k = strchr(str, ',');
403
404                 if (k)
405                         *k++ = 0;
406
407                 parse_memmap_one(str);
408                 str = k;
409         }
410
411         return 0;
412 }
413 early_param("memmap", parse_memmap_opt);