more changes on original files
[linux-2.4.git] / arch / ppc64 / kernel / lmb.c
1 /*
2  *
3  * Procedures for interfacing to Open Firmware.
4  *
5  * Peter Bergner, IBM Corp.     June 2001.
6  * Copyright (C) 2001 Peter Bergner.
7  * 
8  *      This program is free software; you can redistribute it and/or
9  *      modify it under the terms of the GNU General Public License
10  *      as published by the Free Software Foundation; either version
11  *      2 of the License, or (at your option) any later version.
12  */
13
14 #include <linux/config.h>
15 #include <linux/kernel.h>
16 #include <asm/types.h>
17 #include <asm/page.h>
18 #include <asm/prom.h>
19 #include <asm/lmb.h>
20 #include <asm/abs_addr.h>
21 #include <asm/bitops.h>
22 #include <asm/udbg.h>
23
24 extern unsigned long klimit;
25 extern unsigned long reloc_offset(void);
26
27
28 static long lmb_add_region(struct lmb_region *, unsigned long, unsigned long, unsigned long);
29
30 struct lmb lmb = {
31         0, 0,
32         {0,0,0,0,{{0,0,0}}},
33         {0,0,0,0,{{0,0,0}}}
34 };
35
36
37 /* Assumption: base addr of region 1 < base addr of region 2 */
38 static void
39 lmb_coalesce_regions(struct lmb_region *rgn, unsigned long r1, unsigned long r2)
40 {
41         unsigned long i;
42
43         rgn->region[r1].size += rgn->region[r2].size;
44         for (i=r2; i < rgn->cnt-1 ;i++) {
45                 rgn->region[i].base = rgn->region[i+1].base;
46                 rgn->region[i].physbase = rgn->region[i+1].physbase;
47                 rgn->region[i].size = rgn->region[i+1].size;
48                 rgn->region[i].type = rgn->region[i+1].type;
49         }
50         rgn->cnt--;
51 }
52
53
54 /* This routine called with relocation disabled. */
55 void
56 lmb_init(void)
57 {
58         unsigned long offset = reloc_offset();
59         struct lmb *_lmb = PTRRELOC(&lmb);
60
61         /* Create a dummy zero size LMB which will get coalesced away later.
62          * This simplifies the lmb_add() code below...
63          */
64         _lmb->memory.region[0].base = 0;
65         _lmb->memory.region[0].size = 0;
66         _lmb->memory.region[0].type = LMB_MEMORY_AREA;
67         _lmb->memory.cnt = 1;
68
69         /* Ditto. */
70         _lmb->reserved.region[0].base = 0;
71         _lmb->reserved.region[0].size = 0;
72         _lmb->reserved.region[0].type = LMB_MEMORY_AREA;
73         _lmb->reserved.cnt = 1;
74 }
75
76 /* This routine called with relocation disabled. */
77 void
78 lmb_analyze(void)
79 {
80         unsigned long i;
81         unsigned long mem_size = 0;
82         unsigned long io_size = 0;
83         unsigned long size_mask = 0;
84         unsigned long offset = reloc_offset();
85         struct lmb *_lmb = PTRRELOC(&lmb);
86 #ifdef CONFIG_MSCHUNKS
87         unsigned long physbase = 0;
88 #endif
89
90         for (i=0; i < _lmb->memory.cnt ;i++) {
91                 unsigned long lmb_type = _lmb->memory.region[i].type;
92                 unsigned long lmb_size;
93
94                 if ( lmb_type != LMB_MEMORY_AREA )
95                         continue;
96
97                 lmb_size = _lmb->memory.region[i].size;
98
99 #ifdef CONFIG_MSCHUNKS
100                 _lmb->memory.region[i].physbase = physbase;
101                 physbase += lmb_size;
102 #else
103                 _lmb->memory.region[i].physbase = _lmb->memory.region[i].base;
104 #endif
105                 mem_size += lmb_size;
106                 size_mask |= lmb_size;
107         }
108
109 #ifdef CONFIG_MSCHUNKS
110         for (i=0; i < _lmb->memory.cnt ;i++) {
111                 unsigned long lmb_type = _lmb->memory.region[i].type;
112                 unsigned long lmb_size;
113
114                 if ( lmb_type != LMB_IO_AREA )
115                         continue;
116
117                 lmb_size = _lmb->memory.region[i].size;
118
119                 _lmb->memory.region[i].physbase = physbase;
120                 physbase += lmb_size;
121                 io_size += lmb_size;
122                 size_mask |= lmb_size;
123         }
124 #endif /* CONFIG_MSCHUNKS */
125
126         _lmb->memory.size = mem_size;
127         _lmb->memory.iosize = io_size;
128         _lmb->memory.lcd_size = (1UL << cnt_trailing_zeros(size_mask));
129 }
130
131 /* This routine called with relocation disabled. */
132 long
133 lmb_add(unsigned long base, unsigned long size)
134 {
135         unsigned long offset = reloc_offset();
136         struct lmb *_lmb = PTRRELOC(&lmb);
137         struct lmb_region *_rgn = &(_lmb->memory);
138
139         /* On pSeries LPAR systems, the first LMB is our RMO region. */
140         if ( base == 0 )
141                 _lmb->rmo_size = size;
142
143         return lmb_add_region(_rgn, base, size, LMB_MEMORY_AREA);
144
145 }
146
147 #ifdef CONFIG_MSCHUNKS
148 /* This routine called with relocation disabled. */
149 long
150 lmb_add_io(unsigned long base, unsigned long size)
151 {
152         unsigned long offset = reloc_offset();
153         struct lmb *_lmb = PTRRELOC(&lmb);
154         struct lmb_region *_rgn = &(_lmb->memory);
155
156         return lmb_add_region(_rgn, base, size, LMB_IO_AREA);
157
158 }
159 #endif /* CONFIG_MSCHUNKS */
160
161 long
162 lmb_reserve(unsigned long base, unsigned long size)
163 {
164         unsigned long offset = reloc_offset();
165         struct lmb *_lmb = PTRRELOC(&lmb);
166         struct lmb_region *_rgn = &(_lmb->reserved);
167
168         return lmb_add_region(_rgn, base, size, LMB_MEMORY_AREA);
169 }
170
171 /* This routine called with relocation disabled. */
172 static long
173 lmb_add_region(struct lmb_region *rgn, unsigned long base, unsigned long size,
174                 unsigned long type)
175 {
176         unsigned long i, coalesced = 0;
177         long adjacent;
178
179         /* First try and coalesce this LMB with another. */
180         for (i=0; i < rgn->cnt ;i++) {
181                 unsigned long rgnbase = rgn->region[i].base;
182                 unsigned long rgnsize = rgn->region[i].size;
183                 unsigned long rgntype = rgn->region[i].type;
184
185                 if ( rgntype != type )
186                         continue;
187
188                 adjacent = lmb_addrs_adjacent(base,size,rgnbase,rgnsize);
189                 if ( adjacent > 0 ) {
190                         rgn->region[i].base -= size;
191                         rgn->region[i].physbase -= size;
192                         rgn->region[i].size += size;
193                         coalesced++;
194                         break;
195                 }
196                 else if ( adjacent < 0 ) {
197                         rgn->region[i].size += size;
198                         coalesced++;
199                         break;
200                 }
201         }
202
203         if ((i < rgn->cnt-1) && lmb_regions_adjacent(rgn, i, i+1) ) {
204                 lmb_coalesce_regions(rgn, i, i+1);
205                 coalesced++;
206         }
207
208         if ( coalesced ) {
209                 return coalesced;
210         } else if ( rgn->cnt >= MAX_LMB_REGIONS ) {
211                 return -1;
212         }
213
214         /* Couldn't coalesce the LMB, so add it to the sorted table. */
215         for (i=rgn->cnt-1; i >= 0 ;i--) {
216                 if (base < rgn->region[i].base) {
217                         rgn->region[i+1].base = rgn->region[i].base;
218                         rgn->region[i+1].physbase = rgn->region[i].physbase;
219                         rgn->region[i+1].size = rgn->region[i].size;
220                         rgn->region[i+1].type = rgn->region[i].type;
221                 }  else {
222                         rgn->region[i+1].base = base;
223                         rgn->region[i+1].physbase = lmb_abs_to_phys(base);
224                         rgn->region[i+1].size = size;
225                         rgn->region[i+1].type = type;
226                         break;
227                 }
228         }
229         rgn->cnt++;
230
231         return 0;
232 }
233
234 long
235 lmb_overlaps_region(struct lmb_region *rgn, unsigned long base, unsigned long size)
236 {
237         unsigned long i;
238
239         for (i=0; i < rgn->cnt ;i++) {
240                 unsigned long rgnbase = rgn->region[i].base;
241                 unsigned long rgnsize = rgn->region[i].size;
242                 if ( lmb_addrs_overlap(base,size,rgnbase,rgnsize) ) {
243                         break;
244                 }
245         }
246
247         return (i < rgn->cnt) ? i : -1;
248 }
249
250 unsigned long
251 lmb_alloc(unsigned long size, unsigned long align)
252 {
253         return lmb_alloc_base(size, align, LMB_ALLOC_ANYWHERE);
254 }
255
256 unsigned long
257 lmb_alloc_base(unsigned long size, unsigned long align, unsigned long max_addr)
258 {
259         long i, j;
260         unsigned long base = 0;
261         unsigned long offset = reloc_offset();
262         struct lmb *_lmb = PTRRELOC(&lmb);
263         struct lmb_region *_mem = &(_lmb->memory);
264         struct lmb_region *_rsv = &(_lmb->reserved);
265
266         for (i=_mem->cnt-1; i >= 0 ;i--) {
267                 unsigned long lmbbase = _mem->region[i].base;
268                 unsigned long lmbsize = _mem->region[i].size;
269                 unsigned long lmbtype = _mem->region[i].type;
270
271                 if ( lmbtype != LMB_MEMORY_AREA )
272                         continue;
273
274                 if ( max_addr == LMB_ALLOC_ANYWHERE )
275                         base = _ALIGN_DOWN(lmbbase+lmbsize-size, align);
276                 else if ( lmbbase < max_addr )
277                         base = _ALIGN_DOWN(min(lmbbase+lmbsize,max_addr)-size, align);
278                 else
279                         continue;
280
281                 while ( (lmbbase <= base) &&
282                         ((j = lmb_overlaps_region(_rsv,base,size)) >= 0) ) {
283                         base = _ALIGN_DOWN(_rsv->region[j].base-size, align);
284                 }
285
286                 if ( (base != 0) && (lmbbase <= base) )
287                         break;
288         }
289
290         if ( i < 0 )
291                 return 0;
292
293         lmb_add_region(_rsv, base, size, LMB_MEMORY_AREA);
294
295         return base;
296 }
297
298 unsigned long
299 lmb_phys_mem_size(void)
300 {
301         unsigned long offset = reloc_offset();
302         struct lmb *_lmb = PTRRELOC(&lmb);
303 #ifdef CONFIG_MSCHUNKS
304         return _lmb->memory.size;
305 #else
306         struct lmb_region *_mem = &(_lmb->memory);
307         unsigned long idx = _mem->cnt-1;
308         unsigned long lastbase = _mem->region[idx].physbase;
309         unsigned long lastsize = _mem->region[idx].size;
310         
311         return (lastbase + lastsize);
312 #endif /* CONFIG_MSCHUNKS */
313 }
314
315 unsigned long
316 lmb_end_of_DRAM(void)
317 {
318         unsigned long offset = reloc_offset();
319         struct lmb *_lmb = PTRRELOC(&lmb);
320         struct lmb_region *_mem = &(_lmb->memory);
321         unsigned long idx;
322
323         for(idx=_mem->cnt-1; idx >= 0 ;idx--) {
324                 if ( _mem->region[idx].type != LMB_MEMORY_AREA )
325                         continue;
326 #ifdef CONFIG_MSCHUNKS
327                 return (_mem->region[idx].physbase + _mem->region[idx].size);
328 #else
329                 return (_mem->region[idx].base + _mem->region[idx].size);
330 #endif /* CONFIG_MSCHUNKS */
331         }
332
333         return 0;
334 }
335
336
337 unsigned long
338 lmb_abs_to_phys(unsigned long aa)
339 {
340         unsigned long i, pa = aa;
341         unsigned long offset = reloc_offset();
342         struct lmb *_lmb = PTRRELOC(&lmb);
343         struct lmb_region *_mem = &(_lmb->memory);
344
345         for (i=0; i < _mem->cnt ;i++) {
346                 unsigned long lmbbase = _mem->region[i].base;
347                 unsigned long lmbsize = _mem->region[i].size;
348                 if ( lmb_addrs_overlap(aa,1,lmbbase,lmbsize) ) {
349                         pa = _mem->region[i].physbase + (aa - lmbbase);
350                         break;
351                 }
352         }
353
354         return pa;
355 }
356
357 void
358 lmb_dump(char *str)
359 {
360         unsigned long i;
361
362         udbg_printf("\nlmb_dump: %s\n", str);
363         udbg_printf("    debug                       = %s\n",
364                 (lmb.debug) ? "TRUE" : "FALSE");
365         udbg_printf("    memory.cnt                  = %d\n",
366                 lmb.memory.cnt);
367         udbg_printf("    memory.size                 = 0x%lx\n",
368                 lmb.memory.size);
369         udbg_printf("    memory.lcd_size             = 0x%lx\n",
370                 lmb.memory.lcd_size);
371         for (i=0; i < lmb.memory.cnt ;i++) {
372                 udbg_printf("    memory.region[%d].base       = 0x%lx\n",
373                         i, lmb.memory.region[i].base);
374                 udbg_printf("                      .physbase = 0x%lx\n",
375                         lmb.memory.region[i].physbase);
376                 udbg_printf("                      .size     = 0x%lx\n",
377                         lmb.memory.region[i].size);
378                 udbg_printf("                      .type     = 0x%lx\n",
379                         lmb.memory.region[i].type);
380         }
381
382         udbg_printf("\n");
383         udbg_printf("    reserved.cnt                = %d\n",
384                 lmb.reserved.cnt);
385         udbg_printf("    reserved.size               = 0x%lx\n",
386                 lmb.reserved.size);
387         udbg_printf("    reserved.lcd_size           = 0x%lx\n",
388                 lmb.reserved.lcd_size);
389         for (i=0; i < lmb.reserved.cnt ;i++) {
390                 udbg_printf("    reserved.region[%d].base     = 0x%lx\n",
391                         i, lmb.reserved.region[i].base);
392                 udbg_printf("                      .physbase = 0x%lx\n",
393                         lmb.reserved.region[i].physbase);
394                 udbg_printf("                      .size     = 0x%lx\n",
395                         lmb.reserved.region[i].size);
396                 udbg_printf("                      .type     = 0x%lx\n",
397                         lmb.reserved.region[i].type);
398         }
399 }