powerpc: Merge lmb.c and make MM initialization use it.
[linux-2.6.git] / arch / powerpc / mm / lmb.c
1 /*
2  * Procedures for maintaining information about logical memory blocks.
3  *
4  * Peter Bergner, IBM Corp.     June 2001.
5  * Copyright (C) 2001 Peter Bergner.
6  * 
7  *      This program is free software; you can redistribute it and/or
8  *      modify it under the terms of the GNU General Public License
9  *      as published by the Free Software Foundation; either version
10  *      2 of the License, or (at your option) any later version.
11  */
12
13 #include <linux/config.h>
14 #include <linux/kernel.h>
15 #include <linux/init.h>
16 #include <linux/bitops.h>
17 #include <asm/types.h>
18 #include <asm/page.h>
19 #include <asm/prom.h>
20 #include <asm/lmb.h>
21 #ifdef CONFIG_PPC32
22 #include "mmu_decl.h"           /* for __max_low_memory */
23 #endif
24
25 struct lmb lmb;
26
27 #undef DEBUG
28
29 void lmb_dump_all(void)
30 {
31 #ifdef DEBUG
32         unsigned long i;
33
34         udbg_printf("lmb_dump_all:\n");
35         udbg_printf("    memory.cnt               = 0x%lx\n",
36                     lmb.memory.cnt);
37         udbg_printf("    memory.size              = 0x%lx\n",
38                     lmb.memory.size);
39         for (i=0; i < lmb.memory.cnt ;i++) {
40                 udbg_printf("    memory.region[0x%x].base       = 0x%lx\n",
41                             i, lmb.memory.region[i].base);
42                 udbg_printf("                 .size     = 0x%lx\n",
43                             lmb.memory.region[i].size);
44         }
45
46         udbg_printf("\n    reserved.cnt   = 0x%lx\n",
47                     lmb.reserved.cnt);
48         udbg_printf("    reserved.size    = 0x%lx\n",
49                     lmb.reserved.size);
50         for (i=0; i < lmb.reserved.cnt ;i++) {
51                 udbg_printf("    reserved.region[0x%x].base       = 0x%lx\n",
52                             i, lmb.reserved.region[i].base);
53                 udbg_printf("                 .size     = 0x%lx\n",
54                             lmb.reserved.region[i].size);
55         }
56 #endif /* DEBUG */
57 }
58
59 static unsigned long __init lmb_addrs_overlap(unsigned long base1,
60                 unsigned long size1, unsigned long base2, unsigned long size2)
61 {
62         return ((base1 < (base2+size2)) && (base2 < (base1+size1)));
63 }
64
65 static long __init lmb_addrs_adjacent(unsigned long base1, unsigned long size1,
66                 unsigned long base2, unsigned long size2)
67 {
68         if (base2 == base1 + size1)
69                 return 1;
70         else if (base1 == base2 + size2)
71                 return -1;
72
73         return 0;
74 }
75
76 static long __init lmb_regions_adjacent(struct lmb_region *rgn,
77                 unsigned long r1, unsigned long r2)
78 {
79         unsigned long base1 = rgn->region[r1].base;
80         unsigned long size1 = rgn->region[r1].size;
81         unsigned long base2 = rgn->region[r2].base;
82         unsigned long size2 = rgn->region[r2].size;
83
84         return lmb_addrs_adjacent(base1, size1, base2, size2);
85 }
86
87 /* Assumption: base addr of region 1 < base addr of region 2 */
88 static void __init lmb_coalesce_regions(struct lmb_region *rgn,
89                 unsigned long r1, unsigned long r2)
90 {
91         unsigned long i;
92
93         rgn->region[r1].size += rgn->region[r2].size;
94         for (i=r2; i < rgn->cnt-1; i++) {
95                 rgn->region[i].base = rgn->region[i+1].base;
96                 rgn->region[i].size = rgn->region[i+1].size;
97         }
98         rgn->cnt--;
99 }
100
101 /* This routine called with relocation disabled. */
102 void __init lmb_init(void)
103 {
104         /* Create a dummy zero size LMB which will get coalesced away later.
105          * This simplifies the lmb_add() code below...
106          */
107         lmb.memory.region[0].base = 0;
108         lmb.memory.region[0].size = 0;
109         lmb.memory.cnt = 1;
110
111         /* Ditto. */
112         lmb.reserved.region[0].base = 0;
113         lmb.reserved.region[0].size = 0;
114         lmb.reserved.cnt = 1;
115 }
116
117 /* This routine may be called with relocation disabled. */
118 void __init lmb_analyze(void)
119 {
120         int i;
121
122         lmb.memory.size = 0;
123
124         for (i = 0; i < lmb.memory.cnt; i++)
125                 lmb.memory.size += lmb.memory.region[i].size;
126 }
127
128 /* This routine called with relocation disabled. */
129 static long __init lmb_add_region(struct lmb_region *rgn, unsigned long base,
130                                   unsigned long size)
131 {
132         unsigned long i, coalesced = 0;
133         long adjacent;
134
135         /* First try and coalesce this LMB with another. */
136         for (i=0; i < rgn->cnt; i++) {
137                 unsigned long rgnbase = rgn->region[i].base;
138                 unsigned long rgnsize = rgn->region[i].size;
139
140                 adjacent = lmb_addrs_adjacent(base,size,rgnbase,rgnsize);
141                 if ( adjacent > 0 ) {
142                         rgn->region[i].base -= size;
143                         rgn->region[i].size += size;
144                         coalesced++;
145                         break;
146                 }
147                 else if ( adjacent < 0 ) {
148                         rgn->region[i].size += size;
149                         coalesced++;
150                         break;
151                 }
152         }
153
154         if ((i < rgn->cnt-1) && lmb_regions_adjacent(rgn, i, i+1) ) {
155                 lmb_coalesce_regions(rgn, i, i+1);
156                 coalesced++;
157         }
158
159         if (coalesced)
160                 return coalesced;
161         if (rgn->cnt >= MAX_LMB_REGIONS)
162                 return -1;
163
164         /* Couldn't coalesce the LMB, so add it to the sorted table. */
165         for (i = rgn->cnt-1; i >= 0; i--) {
166                 if (base < rgn->region[i].base) {
167                         rgn->region[i+1].base = rgn->region[i].base;
168                         rgn->region[i+1].size = rgn->region[i].size;
169                 } else {
170                         rgn->region[i+1].base = base;
171                         rgn->region[i+1].size = size;
172                         break;
173                 }
174         }
175         rgn->cnt++;
176
177         return 0;
178 }
179
180 /* This routine may be called with relocation disabled. */
181 long __init lmb_add(unsigned long base, unsigned long size)
182 {
183         struct lmb_region *_rgn = &(lmb.memory);
184
185         /* On pSeries LPAR systems, the first LMB is our RMO region. */
186         if (base == 0)
187                 lmb.rmo_size = size;
188
189         return lmb_add_region(_rgn, base, size);
190
191 }
192
193 long __init lmb_reserve(unsigned long base, unsigned long size)
194 {
195         struct lmb_region *_rgn = &(lmb.reserved);
196
197         return lmb_add_region(_rgn, base, size);
198 }
199
200 long __init lmb_overlaps_region(struct lmb_region *rgn, unsigned long base,
201                                 unsigned long size)
202 {
203         unsigned long i;
204
205         for (i=0; i < rgn->cnt; i++) {
206                 unsigned long rgnbase = rgn->region[i].base;
207                 unsigned long rgnsize = rgn->region[i].size;
208                 if ( lmb_addrs_overlap(base,size,rgnbase,rgnsize) ) {
209                         break;
210                 }
211         }
212
213         return (i < rgn->cnt) ? i : -1;
214 }
215
216 unsigned long __init lmb_alloc(unsigned long size, unsigned long align)
217 {
218         return lmb_alloc_base(size, align, LMB_ALLOC_ANYWHERE);
219 }
220
221 unsigned long __init lmb_alloc_base(unsigned long size, unsigned long align,
222                                     unsigned long max_addr)
223 {
224         long i, j;
225         unsigned long base = 0;
226
227 #ifdef CONFIG_PPC32
228         /* On 32-bit, make sure we allocate lowmem */
229         if (max_addr == LMB_ALLOC_ANYWHERE)
230                 max_addr = __max_low_memory;
231 #endif
232         for (i = lmb.memory.cnt-1; i >= 0; i--) {
233                 unsigned long lmbbase = lmb.memory.region[i].base;
234                 unsigned long lmbsize = lmb.memory.region[i].size;
235
236                 if (max_addr == LMB_ALLOC_ANYWHERE)
237                         base = _ALIGN_DOWN(lmbbase + lmbsize - size, align);
238                 else if (lmbbase < max_addr) {
239                         base = min(lmbbase + lmbsize, max_addr);
240                         base = _ALIGN_DOWN(base - size, align);
241                 } else
242                         continue;
243
244                 while ((lmbbase <= base) &&
245                        ((j = lmb_overlaps_region(&lmb.reserved, base, size)) >= 0) )
246                         base = _ALIGN_DOWN(lmb.reserved.region[j].base - size,
247                                            align);
248
249                 if ((base != 0) && (lmbbase <= base))
250                         break;
251         }
252
253         if (i < 0)
254                 return 0;
255
256         lmb_add_region(&lmb.reserved, base, size);
257
258         return base;
259 }
260
261 /* You must call lmb_analyze() before this. */
262 unsigned long __init lmb_phys_mem_size(void)
263 {
264         return lmb.memory.size;
265 }
266
267 unsigned long __init lmb_end_of_DRAM(void)
268 {
269         int idx = lmb.memory.cnt - 1;
270
271         return (lmb.memory.region[idx].base + lmb.memory.region[idx].size);
272 }
273
274 /*
275  * Truncate the lmb list to memory_limit if it's set
276  * You must call lmb_analyze() after this.
277  */
278 void __init lmb_enforce_memory_limit(unsigned long memory_limit)
279 {
280         unsigned long i, limit;
281
282         if (! memory_limit)
283                 return;
284
285         limit = memory_limit;
286         for (i = 0; i < lmb.memory.cnt; i++) {
287                 if (limit > lmb.memory.region[i].size) {
288                         limit -= lmb.memory.region[i].size;
289                         continue;
290                 }
291
292                 lmb.memory.region[i].size = limit;
293                 lmb.memory.cnt = i + 1;
294                 break;
295         }
296 }