Commit | Line | Data |
---|---|---|
a3a0f8c8 DV |
1 | /* |
2 | * Carsten Langgaard, carstenl@mips.com | |
3 | * Copyright (C) 1999,2000 MIPS Technologies, Inc. All rights reserved. | |
4 | * Portions copyright (C) 2009 Cisco Systems, Inc. | |
5 | * | |
6 | * This program is free software; you can distribute it and/or modify it | |
7 | * under the terms of the GNU General Public License (Version 2) as | |
8 | * published by the Free Software Foundation. | |
9 | * | |
10 | * This program is distributed in the hope it will be useful, but WITHOUT | |
11 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
12 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License | |
13 | * for more details. | |
14 | * | |
15 | * You should have received a copy of the GNU General Public License along | |
16 | * with this program; if not, write to the Free Software Foundation, Inc., | |
17 | * 59 Temple Place - Suite 330, Boston MA 02111-1307, USA. | |
18 | * | |
19 | * Apparently originally from arch/mips/malta-memory.c. Modified to work | |
20 | * with the PowerTV bootloader. | |
21 | */ | |
22 | #include <linux/init.h> | |
23 | #include <linux/mm.h> | |
24 | #include <linux/bootmem.h> | |
25 | #include <linux/pfn.h> | |
26 | #include <linux/string.h> | |
27 | ||
28 | #include <asm/bootinfo.h> | |
29 | #include <asm/page.h> | |
30 | #include <asm/sections.h> | |
31 | ||
ca36c36b DV |
32 | #include <asm/mach-powertv/asic.h> |
33 | #include <asm/mach-powertv/ioremap.h> | |
a3a0f8c8 DV |
34 | |
35 | #include "init.h" | |
36 | ||
37 | /* Memory constants */ | |
38 | #define KIBIBYTE(n) ((n) * 1024) /* Number of kibibytes */ | |
39 | #define MEBIBYTE(n) ((n) * KIBIBYTE(1024)) /* Number of mebibytes */ | |
ca36c36b | 40 | #define DEFAULT_MEMSIZE MEBIBYTE(128) /* If no memsize provided */ |
a3a0f8c8 | 41 | |
ca36c36b DV |
42 | #define BLDR_SIZE KIBIBYTE(256) /* Memory reserved for bldr */ |
43 | #define RV_SIZE MEBIBYTE(4) /* Size of reset vector */ | |
a3a0f8c8 | 44 | |
ca36c36b DV |
45 | #define LOW_MEM_END 0x20000000 /* Highest low memory address */ |
46 | #define BLDR_ALIAS 0x10000000 /* Bootloader address */ | |
47 | #define RV_PHYS 0x1fc00000 /* Reset vector address */ | |
48 | #define LOW_RAM_END RV_PHYS /* End of real RAM in low mem */ | |
49 | ||
50 | /* | |
51 | * Very low-level conversion from processor physical address to device | |
52 | * DMA address for the first bank of memory. | |
53 | */ | |
54 | #define PHYS_TO_DMA(paddr) ((paddr) + (CONFIG_LOW_RAM_DMA - LOW_RAM_ALIAS)) | |
55 | ||
56 | unsigned long ptv_memsize; | |
57 | ||
58 | /* | |
b595076a | 59 | * struct low_mem_reserved - Items in low memory that are reserved |
ca36c36b DV |
60 | * @start: Physical address of item |
61 | * @size: Size, in bytes, of this item | |
70342287 | 62 | * @is_aliased: True if this is RAM aliased from another location. If false, |
ca36c36b DV |
63 | * it is something other than aliased RAM and the RAM in the |
64 | * unaliased address is still visible outside of low memory. | |
65 | */ | |
66 | struct low_mem_reserved { | |
67 | phys_addr_t start; | |
68 | phys_addr_t size; | |
69 | bool is_aliased; | |
70 | }; | |
71 | ||
72 | /* | |
73 | * Must be in ascending address order | |
74 | */ | |
75 | struct low_mem_reserved low_mem_reserved[] = { | |
76 | {BLDR_ALIAS, BLDR_SIZE, true}, /* Bootloader RAM */ | |
77 | {RV_PHYS, RV_SIZE, false}, /* Reset vector */ | |
78 | }; | |
79 | ||
80 | /* | |
81 | * struct mem_layout - layout of a piece of the system RAM | |
82 | * @phys: Physical address of the start of this piece of RAM. This is the | |
83 | * address at which both the processor and I/O devices see the | |
84 | * RAM. | |
85 | * @alias: Alias of this piece of memory in order to make it appear in | |
86 | * the low memory part of the processor's address space. I/O | |
87 | * devices don't see anything here. | |
88 | * @size: Size, in bytes, of this piece of RAM | |
89 | */ | |
90 | struct mem_layout { | |
91 | phys_addr_t phys; | |
92 | phys_addr_t alias; | |
93 | phys_addr_t size; | |
94 | }; | |
95 | ||
96 | /* | |
97 | * struct mem_layout_list - list descriptor for layouts of system RAM pieces | |
98 | * @family: Specifies the family being described | |
99 | * @n: Number of &struct mem_layout elements | |
100 | * @layout: Pointer to the list of &mem_layout structures | |
101 | */ | |
102 | struct mem_layout_list { | |
103 | enum family_type family; | |
104 | size_t n; | |
105 | struct mem_layout *layout; | |
106 | }; | |
107 | ||
108 | static struct mem_layout f1500_layout[] = { | |
109 | {0x20000000, 0x10000000, MEBIBYTE(256)}, | |
110 | }; | |
111 | ||
112 | static struct mem_layout f4500_layout[] = { | |
113 | {0x40000000, 0x10000000, MEBIBYTE(256)}, | |
114 | {0x20000000, 0x20000000, MEBIBYTE(32)}, | |
115 | }; | |
116 | ||
117 | static struct mem_layout f8500_layout[] = { | |
118 | {0x40000000, 0x10000000, MEBIBYTE(256)}, | |
119 | {0x20000000, 0x20000000, MEBIBYTE(32)}, | |
120 | {0x30000000, 0x30000000, MEBIBYTE(32)}, | |
121 | }; | |
122 | ||
123 | static struct mem_layout fx600_layout[] = { | |
124 | {0x20000000, 0x10000000, MEBIBYTE(256)}, | |
125 | {0x60000000, 0x60000000, MEBIBYTE(128)}, | |
126 | }; | |
127 | ||
128 | static struct mem_layout_list layout_list[] = { | |
129 | {FAMILY_1500, ARRAY_SIZE(f1500_layout), f1500_layout}, | |
130 | {FAMILY_1500VZE, ARRAY_SIZE(f1500_layout), f1500_layout}, | |
131 | {FAMILY_1500VZF, ARRAY_SIZE(f1500_layout), f1500_layout}, | |
132 | {FAMILY_4500, ARRAY_SIZE(f4500_layout), f4500_layout}, | |
133 | {FAMILY_8500, ARRAY_SIZE(f8500_layout), f8500_layout}, | |
134 | {FAMILY_8500RNG, ARRAY_SIZE(f8500_layout), f8500_layout}, | |
135 | {FAMILY_4600, ARRAY_SIZE(fx600_layout), fx600_layout}, | |
136 | {FAMILY_4600VZA, ARRAY_SIZE(fx600_layout), fx600_layout}, | |
137 | {FAMILY_8600, ARRAY_SIZE(fx600_layout), fx600_layout}, | |
138 | {FAMILY_8600VZB, ARRAY_SIZE(fx600_layout), fx600_layout}, | |
139 | }; | |
140 | ||
141 | /* If we can't determine the layout, use this */ | |
142 | static struct mem_layout default_layout[] = { | |
143 | {0x20000000, 0x10000000, MEBIBYTE(128)}, | |
144 | }; | |
145 | ||
146 | /** | |
147 | * register_non_ram - register low memory not available for RAM usage | |
148 | */ | |
149 | static __init void register_non_ram(void) | |
150 | { | |
151 | int i; | |
152 | ||
153 | for (i = 0; i < ARRAY_SIZE(low_mem_reserved); i++) | |
154 | add_memory_region(low_mem_reserved[i].start, | |
155 | low_mem_reserved[i].size, BOOT_MEM_RESERVED); | |
156 | } | |
157 | ||
158 | /** | |
159 | * get_memsize - get the size of memory as a single bank | |
160 | */ | |
161 | static phys_addr_t get_memsize(void) | |
a3a0f8c8 | 162 | { |
ca36c36b DV |
163 | static char cmdline[COMMAND_LINE_SIZE] __initdata; |
164 | phys_addr_t memsize = 0; | |
a3a0f8c8 | 165 | char *memsize_str; |
a3a0f8c8 | 166 | char *ptr; |
a3a0f8c8 DV |
167 | |
168 | /* Check the command line first for a memsize directive */ | |
169 | strcpy(cmdline, arcs_cmdline); | |
170 | ptr = strstr(cmdline, "memsize="); | |
171 | if (ptr && (ptr != cmdline) && (*(ptr - 1) != ' ')) | |
172 | ptr = strstr(ptr, " memsize="); | |
173 | ||
174 | if (ptr) { | |
175 | memsize = memparse(ptr + 8, &ptr); | |
176 | } else { | |
177 | /* otherwise look in the environment */ | |
178 | memsize_str = prom_getenv("memsize"); | |
179 | ||
180 | if (memsize_str != NULL) { | |
181 | pr_info("prom memsize = %s\n", memsize_str); | |
182 | memsize = simple_strtol(memsize_str, NULL, 0); | |
183 | } | |
184 | ||
185 | if (memsize == 0) { | |
186 | if (_prom_memsize != 0) { | |
187 | memsize = _prom_memsize; | |
ca36c36b | 188 | pr_info("_prom_memsize = 0x%x\n", memsize); |
a3a0f8c8 DV |
189 | /* add in memory that the bootloader doesn't |
190 | * report */ | |
ca36c36b | 191 | memsize += BLDR_SIZE; |
a3a0f8c8 DV |
192 | } else { |
193 | memsize = DEFAULT_MEMSIZE; | |
194 | pr_info("Memsize not passed by bootloader, " | |
ca36c36b | 195 | "defaulting to 0x%x\n", memsize); |
a3a0f8c8 DV |
196 | } |
197 | } | |
198 | } | |
199 | ||
ca36c36b DV |
200 | return memsize; |
201 | } | |
202 | ||
203 | /** | |
204 | * register_low_ram - register an aliased section of RAM | |
205 | * @p: Alias address of memory | |
206 | * @n: Number of bytes in this section of memory | |
207 | * | |
208 | * Returns the number of bytes registered | |
209 | * | |
210 | */ | |
211 | static __init phys_addr_t register_low_ram(phys_addr_t p, phys_addr_t n) | |
212 | { | |
213 | phys_addr_t s; | |
214 | int i; | |
215 | phys_addr_t orig_n; | |
216 | ||
217 | orig_n = n; | |
218 | ||
219 | BUG_ON(p + n > RV_PHYS); | |
220 | ||
221 | for (i = 0; n != 0 && i < ARRAY_SIZE(low_mem_reserved); i++) { | |
222 | phys_addr_t start; | |
223 | phys_addr_t size; | |
224 | ||
225 | start = low_mem_reserved[i].start; | |
226 | size = low_mem_reserved[i].size; | |
227 | ||
228 | /* Handle memory before this low memory section */ | |
229 | if (p < start) { | |
230 | phys_addr_t s; | |
231 | s = min(n, start - p); | |
232 | add_memory_region(p, s, BOOT_MEM_RAM); | |
233 | p += s; | |
234 | n -= s; | |
235 | } | |
236 | ||
237 | /* Handle the low memory section itself. If it's aliased, | |
238 | * we reduce the number of byes left, but if not, the RAM | |
239 | * is available elsewhere and we don't reduce the number of | |
240 | * bytes remaining. */ | |
241 | if (p == start) { | |
242 | if (low_mem_reserved[i].is_aliased) { | |
243 | s = min(n, size); | |
244 | n -= s; | |
245 | p += s; | |
246 | } else | |
247 | p += n; | |
248 | } | |
a3a0f8c8 DV |
249 | } |
250 | ||
ca36c36b DV |
251 | return orig_n - n; |
252 | } | |
253 | ||
a3a0f8c8 | 254 | /* |
ca36c36b DV |
255 | * register_ram - register real RAM |
256 | * @p: Address of memory as seen by devices | |
257 | * @alias: If the memory is seen at an additional address by the processor, | |
258 | * this will be the address, otherwise it is the same as @p. | |
259 | * @n: Number of bytes in this section of memory | |
a3a0f8c8 | 260 | */ |
ca36c36b DV |
261 | static __init void register_ram(phys_addr_t p, phys_addr_t alias, |
262 | phys_addr_t n) | |
263 | { | |
a3a0f8c8 | 264 | /* |
ca36c36b DV |
265 | * If some or all of this memory has an alias, break it into the |
266 | * aliased and non-aliased portion. | |
a3a0f8c8 | 267 | */ |
ca36c36b DV |
268 | if (p != alias) { |
269 | phys_addr_t alias_size; | |
270 | phys_addr_t registered; | |
271 | ||
272 | alias_size = min(n, LOW_RAM_END - alias); | |
273 | registered = register_low_ram(alias, alias_size); | |
274 | ioremap_add_map(alias, p, n); | |
275 | n -= registered; | |
276 | p += registered; | |
277 | } | |
278 | ||
279 | #ifdef CONFIG_HIGHMEM | |
280 | if (n != 0) { | |
281 | add_memory_region(p, n, BOOT_MEM_RAM); | |
282 | ioremap_add_map(p, p, n); | |
283 | } | |
284 | #endif | |
285 | } | |
286 | ||
287 | /** | |
288 | * register_address_space - register things in the address space | |
289 | * @memsize: Number of bytes of RAM installed | |
290 | * | |
291 | * Takes the given number of bytes of RAM and registers as many of the regions, | |
292 | * or partial regions, as it can. So, the default configuration might have | |
293 | * two regions with 256 MiB each. If the memsize passed in on the command line | |
294 | * is 384 MiB, it will register the first region with 256 MiB and the second | |
295 | * with 128 MiB. | |
296 | */ | |
297 | static __init void register_address_space(phys_addr_t memsize) | |
298 | { | |
299 | int i; | |
300 | phys_addr_t size; | |
301 | size_t n; | |
302 | struct mem_layout *layout; | |
303 | enum family_type family; | |
304 | ||
a3a0f8c8 | 305 | /* |
ca36c36b DV |
306 | * Register all of the things that aren't available to the kernel as |
307 | * memory. | |
a3a0f8c8 | 308 | */ |
ca36c36b DV |
309 | register_non_ram(); |
310 | ||
311 | /* Find the appropriate memory description */ | |
312 | family = platform_get_family(); | |
313 | ||
314 | for (i = 0; i < ARRAY_SIZE(layout_list); i++) { | |
315 | if (layout_list[i].family == family) | |
316 | break; | |
317 | } | |
318 | ||
319 | if (i == ARRAY_SIZE(layout_list)) { | |
320 | n = ARRAY_SIZE(default_layout); | |
321 | layout = default_layout; | |
322 | } else { | |
323 | n = layout_list[i].n; | |
324 | layout = layout_list[i].layout; | |
325 | } | |
326 | ||
327 | for (i = 0; memsize != 0 && i < n; i++) { | |
328 | size = min(memsize, layout[i].size); | |
329 | register_ram(layout[i].phys, layout[i].alias, size); | |
330 | memsize -= size; | |
331 | } | |
332 | } | |
333 | ||
334 | void __init prom_meminit(void) | |
335 | { | |
336 | ptv_memsize = get_memsize(); | |
337 | register_address_space(ptv_memsize); | |
a3a0f8c8 DV |
338 | } |
339 | ||
340 | void __init prom_free_prom_memory(void) | |
341 | { | |
342 | unsigned long addr; | |
343 | int i; | |
344 | ||
345 | for (i = 0; i < boot_mem_map.nr_map; i++) { | |
346 | if (boot_mem_map.map[i].type != BOOT_MEM_ROM_DATA) | |
347 | continue; | |
348 | ||
349 | addr = boot_mem_map.map[i].addr; | |
350 | free_init_pages("prom memory", | |
351 | addr, addr + boot_mem_map.map[i].size); | |
352 | } | |
353 | } |