Commit | Line | Data |
---|---|---|
5f97f7f9 HS |
1 | /* |
2 | * Copyright (C) 2004-2006 Atmel Corporation | |
3 | * | |
4 | * This program is free software; you can redistribute it and/or modify | |
5 | * it under the terms of the GNU General Public License version 2 as | |
6 | * published by the Free Software Foundation. | |
7 | */ | |
8 | ||
9 | #include <linux/clk.h> | |
10 | #include <linux/init.h> | |
11 | #include <linux/sched.h> | |
12 | #include <linux/console.h> | |
13 | #include <linux/ioport.h> | |
14 | #include <linux/bootmem.h> | |
15 | #include <linux/fs.h> | |
16 | #include <linux/module.h> | |
17 | #include <linux/root_dev.h> | |
18 | #include <linux/cpu.h> | |
19 | ||
20 | #include <asm/sections.h> | |
21 | #include <asm/processor.h> | |
22 | #include <asm/pgtable.h> | |
23 | #include <asm/setup.h> | |
24 | #include <asm/sysreg.h> | |
25 | ||
26 | #include <asm/arch/board.h> | |
27 | #include <asm/arch/init.h> | |
28 | ||
29 | extern int root_mountflags; | |
30 | ||
31 | /* | |
32 | * Bootloader-provided information about physical memory | |
33 | */ | |
34 | struct tag_mem_range *mem_phys; | |
35 | struct tag_mem_range *mem_reserved; | |
36 | struct tag_mem_range *mem_ramdisk; | |
37 | ||
38 | /* | |
39 | * Initialize loops_per_jiffy as 5000000 (500MIPS). | |
40 | * Better make it too large than too small... | |
41 | */ | |
42 | struct avr32_cpuinfo boot_cpu_data = { | |
43 | .loops_per_jiffy = 5000000 | |
44 | }; | |
45 | EXPORT_SYMBOL(boot_cpu_data); | |
46 | ||
47 | static char command_line[COMMAND_LINE_SIZE]; | |
48 | ||
49 | /* | |
50 | * Should be more than enough, but if you have a _really_ complex | |
51 | * setup, you might need to increase the size of this... | |
52 | */ | |
53 | static struct tag_mem_range __initdata mem_range_cache[32]; | |
54 | static unsigned mem_range_next_free; | |
55 | ||
56 | /* | |
57 | * Standard memory resources | |
58 | */ | |
59 | static struct resource mem_res[] = { | |
60 | { | |
61 | .name = "Kernel code", | |
62 | .start = 0, | |
63 | .end = 0, | |
64 | .flags = IORESOURCE_MEM | |
65 | }, | |
66 | { | |
67 | .name = "Kernel data", | |
68 | .start = 0, | |
69 | .end = 0, | |
70 | .flags = IORESOURCE_MEM, | |
71 | }, | |
72 | }; | |
73 | ||
74 | #define kernel_code mem_res[0] | |
75 | #define kernel_data mem_res[1] | |
76 | ||
77 | /* | |
78 | * Early framebuffer allocation. Works as follows: | |
79 | * - If fbmem_size is zero, nothing will be allocated or reserved. | |
80 | * - If fbmem_start is zero when setup_bootmem() is called, | |
81 | * fbmem_size bytes will be allocated from the bootmem allocator. | |
82 | * - If fbmem_start is nonzero, an area of size fbmem_size will be | |
83 | * reserved at the physical address fbmem_start if necessary. If | |
84 | * the area isn't in a memory region known to the kernel, it will | |
85 | * be left alone. | |
86 | * | |
87 | * Board-specific code may use these variables to set up platform data | |
88 | * for the framebuffer driver if fbmem_size is nonzero. | |
89 | */ | |
90 | static unsigned long __initdata fbmem_start; | |
91 | static unsigned long __initdata fbmem_size; | |
92 | ||
93 | /* | |
94 | * "fbmem=xxx[kKmM]" allocates the specified amount of boot memory for | |
95 | * use as framebuffer. | |
96 | * | |
97 | * "fbmem=xxx[kKmM]@yyy[kKmM]" defines a memory region of size xxx and | |
98 | * starting at yyy to be reserved for use as framebuffer. | |
99 | * | |
100 | * The kernel won't verify that the memory region starting at yyy | |
101 | * actually contains usable RAM. | |
102 | */ | |
103 | static int __init early_parse_fbmem(char *p) | |
104 | { | |
105 | fbmem_size = memparse(p, &p); | |
106 | if (*p == '@') | |
107 | fbmem_start = memparse(p, &p); | |
108 | return 0; | |
109 | } | |
110 | early_param("fbmem", early_parse_fbmem); | |
111 | ||
112 | static inline void __init resource_init(void) | |
113 | { | |
114 | struct tag_mem_range *region; | |
115 | ||
116 | kernel_code.start = __pa(init_mm.start_code); | |
117 | kernel_code.end = __pa(init_mm.end_code - 1); | |
118 | kernel_data.start = __pa(init_mm.end_code); | |
119 | kernel_data.end = __pa(init_mm.brk - 1); | |
120 | ||
121 | for (region = mem_phys; region; region = region->next) { | |
122 | struct resource *res; | |
123 | unsigned long phys_start, phys_end; | |
124 | ||
125 | if (region->size == 0) | |
126 | continue; | |
127 | ||
128 | phys_start = region->addr; | |
129 | phys_end = phys_start + region->size - 1; | |
130 | ||
131 | res = alloc_bootmem_low(sizeof(*res)); | |
132 | res->name = "System RAM"; | |
133 | res->start = phys_start; | |
134 | res->end = phys_end; | |
135 | res->flags = IORESOURCE_MEM | IORESOURCE_BUSY; | |
136 | ||
137 | request_resource (&iomem_resource, res); | |
138 | ||
139 | if (kernel_code.start >= res->start && | |
140 | kernel_code.end <= res->end) | |
141 | request_resource (res, &kernel_code); | |
142 | if (kernel_data.start >= res->start && | |
143 | kernel_data.end <= res->end) | |
144 | request_resource (res, &kernel_data); | |
145 | } | |
146 | } | |
147 | ||
148 | static int __init parse_tag_core(struct tag *tag) | |
149 | { | |
150 | if (tag->hdr.size > 2) { | |
151 | if ((tag->u.core.flags & 1) == 0) | |
152 | root_mountflags &= ~MS_RDONLY; | |
153 | ROOT_DEV = new_decode_dev(tag->u.core.rootdev); | |
154 | } | |
155 | return 0; | |
156 | } | |
157 | __tagtable(ATAG_CORE, parse_tag_core); | |
158 | ||
159 | static int __init parse_tag_mem_range(struct tag *tag, | |
160 | struct tag_mem_range **root) | |
161 | { | |
162 | struct tag_mem_range *cur, **pprev; | |
163 | struct tag_mem_range *new; | |
164 | ||
165 | /* | |
166 | * Ignore zero-sized entries. If we're running standalone, the | |
167 | * SDRAM code may emit such entries if something goes | |
168 | * wrong... | |
169 | */ | |
170 | if (tag->u.mem_range.size == 0) | |
171 | return 0; | |
172 | ||
173 | /* | |
174 | * Copy the data so the bootmem init code doesn't need to care | |
175 | * about it. | |
176 | */ | |
177 | if (mem_range_next_free >= | |
178 | (sizeof(mem_range_cache) / sizeof(mem_range_cache[0]))) | |
179 | panic("Physical memory map too complex!\n"); | |
180 | ||
181 | new = &mem_range_cache[mem_range_next_free++]; | |
182 | *new = tag->u.mem_range; | |
183 | ||
184 | pprev = root; | |
185 | cur = *root; | |
186 | while (cur) { | |
187 | pprev = &cur->next; | |
188 | cur = cur->next; | |
189 | } | |
190 | ||
191 | *pprev = new; | |
192 | new->next = NULL; | |
193 | ||
194 | return 0; | |
195 | } | |
196 | ||
197 | static int __init parse_tag_mem(struct tag *tag) | |
198 | { | |
199 | return parse_tag_mem_range(tag, &mem_phys); | |
200 | } | |
201 | __tagtable(ATAG_MEM, parse_tag_mem); | |
202 | ||
203 | static int __init parse_tag_cmdline(struct tag *tag) | |
204 | { | |
205 | strlcpy(saved_command_line, tag->u.cmdline.cmdline, COMMAND_LINE_SIZE); | |
206 | return 0; | |
207 | } | |
208 | __tagtable(ATAG_CMDLINE, parse_tag_cmdline); | |
209 | ||
210 | static int __init parse_tag_rdimg(struct tag *tag) | |
211 | { | |
212 | return parse_tag_mem_range(tag, &mem_ramdisk); | |
213 | } | |
214 | __tagtable(ATAG_RDIMG, parse_tag_rdimg); | |
215 | ||
216 | static int __init parse_tag_clock(struct tag *tag) | |
217 | { | |
218 | /* | |
219 | * We'll figure out the clocks by peeking at the system | |
220 | * manager regs directly. | |
221 | */ | |
222 | return 0; | |
223 | } | |
224 | __tagtable(ATAG_CLOCK, parse_tag_clock); | |
225 | ||
226 | static int __init parse_tag_rsvd_mem(struct tag *tag) | |
227 | { | |
228 | return parse_tag_mem_range(tag, &mem_reserved); | |
229 | } | |
230 | __tagtable(ATAG_RSVD_MEM, parse_tag_rsvd_mem); | |
231 | ||
5f97f7f9 HS |
232 | /* |
233 | * Scan the tag table for this tag, and call its parse function. The | |
234 | * tag table is built by the linker from all the __tagtable | |
235 | * declarations. | |
236 | */ | |
237 | static int __init parse_tag(struct tag *tag) | |
238 | { | |
239 | extern struct tagtable __tagtable_begin, __tagtable_end; | |
240 | struct tagtable *t; | |
241 | ||
242 | for (t = &__tagtable_begin; t < &__tagtable_end; t++) | |
243 | if (tag->hdr.tag == t->tag) { | |
244 | t->parse(tag); | |
245 | break; | |
246 | } | |
247 | ||
248 | return t < &__tagtable_end; | |
249 | } | |
250 | ||
251 | /* | |
252 | * Parse all tags in the list we got from the boot loader | |
253 | */ | |
254 | static void __init parse_tags(struct tag *t) | |
255 | { | |
256 | for (; t->hdr.tag != ATAG_NONE; t = tag_next(t)) | |
257 | if (!parse_tag(t)) | |
258 | printk(KERN_WARNING | |
259 | "Ignoring unrecognised tag 0x%08x\n", | |
260 | t->hdr.tag); | |
261 | } | |
262 | ||
263 | void __init setup_arch (char **cmdline_p) | |
264 | { | |
265 | struct clk *cpu_clk; | |
266 | ||
267 | parse_tags(bootloader_tags); | |
268 | ||
269 | setup_processor(); | |
270 | setup_platform(); | |
c194588d | 271 | setup_board(); |
5f97f7f9 HS |
272 | |
273 | cpu_clk = clk_get(NULL, "cpu"); | |
274 | if (IS_ERR(cpu_clk)) { | |
275 | printk(KERN_WARNING "Warning: Unable to get CPU clock\n"); | |
276 | } else { | |
277 | unsigned long cpu_hz = clk_get_rate(cpu_clk); | |
278 | ||
279 | /* | |
280 | * Well, duh, but it's probably a good idea to | |
281 | * increment the use count. | |
282 | */ | |
283 | clk_enable(cpu_clk); | |
284 | ||
285 | boot_cpu_data.clk = cpu_clk; | |
286 | boot_cpu_data.loops_per_jiffy = cpu_hz * 4; | |
287 | printk("CPU: Running at %lu.%03lu MHz\n", | |
288 | ((cpu_hz + 500) / 1000) / 1000, | |
289 | ((cpu_hz + 500) / 1000) % 1000); | |
290 | } | |
291 | ||
292 | init_mm.start_code = (unsigned long) &_text; | |
293 | init_mm.end_code = (unsigned long) &_etext; | |
294 | init_mm.end_data = (unsigned long) &_edata; | |
295 | init_mm.brk = (unsigned long) &_end; | |
296 | ||
297 | strlcpy(command_line, saved_command_line, COMMAND_LINE_SIZE); | |
298 | *cmdline_p = command_line; | |
299 | parse_early_param(); | |
300 | ||
301 | setup_bootmem(); | |
302 | ||
303 | board_setup_fbmem(fbmem_start, fbmem_size); | |
304 | ||
305 | #ifdef CONFIG_VT | |
306 | conswitchp = &dummy_con; | |
307 | #endif | |
308 | ||
309 | paging_init(); | |
310 | ||
311 | resource_init(); | |
312 | } |