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