/* * Copyright (C) 2004-2006 Atmel Corporation * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include extern int root_mountflags; /* * Bootloader-provided information about physical memory */ struct tag_mem_range *mem_phys; struct tag_mem_range *mem_reserved; struct tag_mem_range *mem_ramdisk; /* * Initialize loops_per_jiffy as 5000000 (500MIPS). * Better make it too large than too small... */ struct avr32_cpuinfo boot_cpu_data = { .loops_per_jiffy = 5000000 }; EXPORT_SYMBOL(boot_cpu_data); static char command_line[COMMAND_LINE_SIZE]; /* * Should be more than enough, but if you have a _really_ complex * setup, you might need to increase the size of this... */ static struct tag_mem_range __initdata mem_range_cache[32]; static unsigned mem_range_next_free; /* * Standard memory resources */ static struct resource mem_res[] = { { .name = "Kernel code", .start = 0, .end = 0, .flags = IORESOURCE_MEM }, { .name = "Kernel data", .start = 0, .end = 0, .flags = IORESOURCE_MEM, }, }; #define kernel_code mem_res[0] #define kernel_data mem_res[1] /* * Early framebuffer allocation. Works as follows: * - If fbmem_size is zero, nothing will be allocated or reserved. * - If fbmem_start is zero when setup_bootmem() is called, * fbmem_size bytes will be allocated from the bootmem allocator. * - If fbmem_start is nonzero, an area of size fbmem_size will be * reserved at the physical address fbmem_start if necessary. If * the area isn't in a memory region known to the kernel, it will * be left alone. * * Board-specific code may use these variables to set up platform data * for the framebuffer driver if fbmem_size is nonzero. */ static unsigned long __initdata fbmem_start; static unsigned long __initdata fbmem_size; /* * "fbmem=xxx[kKmM]" allocates the specified amount of boot memory for * use as framebuffer. * * "fbmem=xxx[kKmM]@yyy[kKmM]" defines a memory region of size xxx and * starting at yyy to be reserved for use as framebuffer. * * The kernel won't verify that the memory region starting at yyy * actually contains usable RAM. */ static int __init early_parse_fbmem(char *p) { fbmem_size = memparse(p, &p); if (*p == '@') fbmem_start = memparse(p, &p); return 0; } early_param("fbmem", early_parse_fbmem); static inline void __init resource_init(void) { struct tag_mem_range *region; kernel_code.start = __pa(init_mm.start_code); kernel_code.end = __pa(init_mm.end_code - 1); kernel_data.start = __pa(init_mm.end_code); kernel_data.end = __pa(init_mm.brk - 1); for (region = mem_phys; region; region = region->next) { struct resource *res; unsigned long phys_start, phys_end; if (region->size == 0) continue; phys_start = region->addr; phys_end = phys_start + region->size - 1; res = alloc_bootmem_low(sizeof(*res)); res->name = "System RAM"; res->start = phys_start; res->end = phys_end; res->flags = IORESOURCE_MEM | IORESOURCE_BUSY; request_resource (&iomem_resource, res); if (kernel_code.start >= res->start && kernel_code.end <= res->end) request_resource (res, &kernel_code); if (kernel_data.start >= res->start && kernel_data.end <= res->end) request_resource (res, &kernel_data); } } static int __init parse_tag_core(struct tag *tag) { if (tag->hdr.size > 2) { if ((tag->u.core.flags & 1) == 0) root_mountflags &= ~MS_RDONLY; ROOT_DEV = new_decode_dev(tag->u.core.rootdev); } return 0; } __tagtable(ATAG_CORE, parse_tag_core); static int __init parse_tag_mem_range(struct tag *tag, struct tag_mem_range **root) { struct tag_mem_range *cur, **pprev; struct tag_mem_range *new; /* * Ignore zero-sized entries. If we're running standalone, the * SDRAM code may emit such entries if something goes * wrong... */ if (tag->u.mem_range.size == 0) return 0; /* * Copy the data so the bootmem init code doesn't need to care * about it. */ if (mem_range_next_free >= (sizeof(mem_range_cache) / sizeof(mem_range_cache[0]))) panic("Physical memory map too complex!\n"); new = &mem_range_cache[mem_range_next_free++]; *new = tag->u.mem_range; pprev = root; cur = *root; while (cur) { pprev = &cur->next; cur = cur->next; } *pprev = new; new->next = NULL; return 0; } static int __init parse_tag_mem(struct tag *tag) { return parse_tag_mem_range(tag, &mem_phys); } __tagtable(ATAG_MEM, parse_tag_mem); static int __init parse_tag_cmdline(struct tag *tag) { strlcpy(saved_command_line, tag->u.cmdline.cmdline, COMMAND_LINE_SIZE); return 0; } __tagtable(ATAG_CMDLINE, parse_tag_cmdline); static int __init parse_tag_rdimg(struct tag *tag) { return parse_tag_mem_range(tag, &mem_ramdisk); } __tagtable(ATAG_RDIMG, parse_tag_rdimg); static int __init parse_tag_clock(struct tag *tag) { /* * We'll figure out the clocks by peeking at the system * manager regs directly. */ return 0; } __tagtable(ATAG_CLOCK, parse_tag_clock); static int __init parse_tag_rsvd_mem(struct tag *tag) { return parse_tag_mem_range(tag, &mem_reserved); } __tagtable(ATAG_RSVD_MEM, parse_tag_rsvd_mem); static int __init parse_tag_ethernet(struct tag *tag) { #if 0 const struct platform_device *pdev; /* * We really need a bus type that supports "classes"...this * will do for now (until we must handle other kinds of * ethernet controllers) */ pdev = platform_get_device("macb", tag->u.ethernet.mac_index); if (pdev && pdev->dev.platform_data) { struct eth_platform_data *data = pdev->dev.platform_data; data->valid = 1; data->mii_phy_addr = tag->u.ethernet.mii_phy_addr; memcpy(data->hw_addr, tag->u.ethernet.hw_address, sizeof(data->hw_addr)); } #endif return 0; } __tagtable(ATAG_ETHERNET, parse_tag_ethernet); /* * Scan the tag table for this tag, and call its parse function. The * tag table is built by the linker from all the __tagtable * declarations. */ static int __init parse_tag(struct tag *tag) { extern struct tagtable __tagtable_begin, __tagtable_end; struct tagtable *t; for (t = &__tagtable_begin; t < &__tagtable_end; t++) if (tag->hdr.tag == t->tag) { t->parse(tag); break; } return t < &__tagtable_end; } /* * Parse all tags in the list we got from the boot loader */ static void __init parse_tags(struct tag *t) { for (; t->hdr.tag != ATAG_NONE; t = tag_next(t)) if (!parse_tag(t)) printk(KERN_WARNING "Ignoring unrecognised tag 0x%08x\n", t->hdr.tag); } void __init setup_arch (char **cmdline_p) { struct clk *cpu_clk; parse_tags(bootloader_tags); setup_processor(); setup_platform(); setup_board(); cpu_clk = clk_get(NULL, "cpu"); if (IS_ERR(cpu_clk)) { printk(KERN_WARNING "Warning: Unable to get CPU clock\n"); } else { unsigned long cpu_hz = clk_get_rate(cpu_clk); /* * Well, duh, but it's probably a good idea to * increment the use count. */ clk_enable(cpu_clk); boot_cpu_data.clk = cpu_clk; boot_cpu_data.loops_per_jiffy = cpu_hz * 4; printk("CPU: Running at %lu.%03lu MHz\n", ((cpu_hz + 500) / 1000) / 1000, ((cpu_hz + 500) / 1000) % 1000); } init_mm.start_code = (unsigned long) &_text; init_mm.end_code = (unsigned long) &_etext; init_mm.end_data = (unsigned long) &_edata; init_mm.brk = (unsigned long) &_end; strlcpy(command_line, saved_command_line, COMMAND_LINE_SIZE); *cmdline_p = command_line; parse_early_param(); setup_bootmem(); board_setup_fbmem(fbmem_start, fbmem_size); #ifdef CONFIG_VT conswitchp = &dummy_con; #endif paging_init(); resource_init(); }