| /* | |
| * 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 <linux/clk.h> | |
| #include <linux/init.h> | |
| #include <linux/initrd.h> | |
| #include <linux/sched.h> | |
| #include <linux/console.h> | |
| #include <linux/ioport.h> | |
| #include <linux/bootmem.h> | |
| #include <linux/fs.h> | |
| #include <linux/module.h> | |
| #include <linux/pfn.h> | |
| #include <linux/root_dev.h> | |
| #include <linux/cpu.h> | |
| #include <linux/kernel.h> | |
| #include <asm/sections.h> | |
| #include <asm/processor.h> | |
| #include <asm/pgtable.h> | |
| #include <asm/setup.h> | |
| #include <asm/sysreg.h> | |
| #include <mach/board.h> | |
| #include <mach/init.h> | |
| extern int root_mountflags; | |
| /* | |
| * 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 __initdata command_line[COMMAND_LINE_SIZE]; | |
| /* | |
| * Standard memory resources | |
| */ | |
| static struct resource __initdata kernel_data = { | |
| .name = "Kernel data", | |
| .start = 0, | |
| .end = 0, | |
| .flags = IORESOURCE_SYSTEM_RAM, | |
| }; | |
| static struct resource __initdata kernel_code = { | |
| .name = "Kernel code", | |
| .start = 0, | |
| .end = 0, | |
| .flags = IORESOURCE_SYSTEM_RAM, | |
| .sibling = &kernel_data, | |
| }; | |
| /* | |
| * Available system RAM and reserved regions as singly linked | |
| * lists. These lists are traversed using the sibling pointer in | |
| * struct resource and are kept sorted at all times. | |
| */ | |
| static struct resource *__initdata system_ram; | |
| static struct resource *__initdata reserved = &kernel_code; | |
| /* | |
| * We need to allocate these before the bootmem allocator is up and | |
| * running, so we need this "cache". 32 entries are probably enough | |
| * for all but the most insanely complex systems. | |
| */ | |
| static struct resource __initdata res_cache[32]; | |
| static unsigned int __initdata res_cache_next_free; | |
| static void __init resource_init(void) | |
| { | |
| struct resource *mem, *res; | |
| struct resource *new; | |
| kernel_code.start = __pa(init_mm.start_code); | |
| for (mem = system_ram; mem; mem = mem->sibling) { | |
| new = alloc_bootmem_low(sizeof(struct resource)); | |
| memcpy(new, mem, sizeof(struct resource)); | |
| new->sibling = NULL; | |
| if (request_resource(&iomem_resource, new)) | |
| printk(KERN_WARNING "Bad RAM resource %08x-%08x\n", | |
| mem->start, mem->end); | |
| } | |
| for (res = reserved; res; res = res->sibling) { | |
| new = alloc_bootmem_low(sizeof(struct resource)); | |
| memcpy(new, res, sizeof(struct resource)); | |
| new->sibling = NULL; | |
| if (insert_resource(&iomem_resource, new)) | |
| printk(KERN_WARNING | |
| "Bad reserved resource %s (%08x-%08x)\n", | |
| res->name, res->start, res->end); | |
| } | |
| } | |
| static void __init | |
| add_physical_memory(resource_size_t start, resource_size_t end) | |
| { | |
| struct resource *new, *next, **pprev; | |
| for (pprev = &system_ram, next = system_ram; next; | |
| pprev = &next->sibling, next = next->sibling) { | |
| if (end < next->start) | |
| break; | |
| if (start <= next->end) { | |
| printk(KERN_WARNING | |
| "Warning: Physical memory map is broken\n"); | |
| printk(KERN_WARNING | |
| "Warning: %08x-%08x overlaps %08x-%08x\n", | |
| start, end, next->start, next->end); | |
| return; | |
| } | |
| } | |
| if (res_cache_next_free >= ARRAY_SIZE(res_cache)) { | |
| printk(KERN_WARNING | |
| "Warning: Failed to add physical memory %08x-%08x\n", | |
| start, end); | |
| return; | |
| } | |
| new = &res_cache[res_cache_next_free++]; | |
| new->start = start; | |
| new->end = end; | |
| new->name = "System RAM"; | |
| new->flags = IORESOURCE_SYSTEM_RAM; | |
| *pprev = new; | |
| } | |
| static int __init | |
| add_reserved_region(resource_size_t start, resource_size_t end, | |
| const char *name) | |
| { | |
| struct resource *new, *next, **pprev; | |
| if (end < start) | |
| return -EINVAL; | |
| if (res_cache_next_free >= ARRAY_SIZE(res_cache)) | |
| return -ENOMEM; | |
| for (pprev = &reserved, next = reserved; next; | |
| pprev = &next->sibling, next = next->sibling) { | |
| if (end < next->start) | |
| break; | |
| if (start <= next->end) | |
| return -EBUSY; | |
| } | |
| new = &res_cache[res_cache_next_free++]; | |
| new->start = start; | |
| new->end = end; | |
| new->name = name; | |
| new->sibling = next; | |
| new->flags = IORESOURCE_MEM; | |
| *pprev = new; | |
| return 0; | |
| } | |
| static unsigned long __init | |
| find_free_region(const struct resource *mem, resource_size_t size, | |
| resource_size_t align) | |
| { | |
| struct resource *res; | |
| unsigned long target; | |
| target = ALIGN(mem->start, align); | |
| for (res = reserved; res; res = res->sibling) { | |
| if ((target + size) <= res->start) | |
| break; | |
| if (target <= res->end) | |
| target = ALIGN(res->end + 1, align); | |
| } | |
| if ((target + size) > (mem->end + 1)) | |
| return mem->end + 1; | |
| return target; | |
| } | |
| static int __init | |
| alloc_reserved_region(resource_size_t *start, resource_size_t size, | |
| resource_size_t align, const char *name) | |
| { | |
| struct resource *mem; | |
| resource_size_t target; | |
| int ret; | |
| for (mem = system_ram; mem; mem = mem->sibling) { | |
| target = find_free_region(mem, size, align); | |
| if (target <= mem->end) { | |
| ret = add_reserved_region(target, target + size - 1, | |
| name); | |
| if (!ret) | |
| *start = target; | |
| return ret; | |
| } | |
| } | |
| return -ENOMEM; | |
| } | |
| /* | |
| * 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, | |
| * a block of fbmem_size bytes will be reserved before bootmem | |
| * initialization. It will be aligned to the largest page size | |
| * that fbmem_size is a multiple of. | |
| * - If fbmem_start is nonzero, an area of size fbmem_size will be | |
| * reserved at the physical address fbmem_start if possible. If | |
| * it collides with other reserved memory, a different block of | |
| * same size will be allocated, just as if fbmem_start was zero. | |
| * | |
| * Board-specific code may use these variables to set up platform data | |
| * for the framebuffer driver if fbmem_size is nonzero. | |
| */ | |
| resource_size_t __initdata fbmem_start; | |
| resource_size_t __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) | |
| { | |
| int ret; | |
| unsigned long align; | |
| fbmem_size = memparse(p, &p); | |
| if (*p == '@') { | |
| fbmem_start = memparse(p + 1, &p); | |
| ret = add_reserved_region(fbmem_start, | |
| fbmem_start + fbmem_size - 1, | |
| "Framebuffer"); | |
| if (ret) { | |
| printk(KERN_WARNING | |
| "Failed to reserve framebuffer memory\n"); | |
| fbmem_start = 0; | |
| } | |
| } | |
| if (!fbmem_start) { | |
| if ((fbmem_size & 0x000fffffUL) == 0) | |
| align = 0x100000; /* 1 MiB */ | |
| else if ((fbmem_size & 0x0000ffffUL) == 0) | |
| align = 0x10000; /* 64 KiB */ | |
| else | |
| align = 0x1000; /* 4 KiB */ | |
| ret = alloc_reserved_region(&fbmem_start, fbmem_size, | |
| align, "Framebuffer"); | |
| if (ret) { | |
| printk(KERN_WARNING | |
| "Failed to allocate framebuffer memory\n"); | |
| fbmem_size = 0; | |
| } else { | |
| memset(__va(fbmem_start), 0, fbmem_size); | |
| } | |
| } | |
| return 0; | |
| } | |
| early_param("fbmem", early_parse_fbmem); | |
| /* | |
| * Pick out the memory size. We look for mem=size@start, | |
| * where start and size are "size[KkMmGg]" | |
| */ | |
| static int __init early_mem(char *p) | |
| { | |
| resource_size_t size, start; | |
| start = system_ram->start; | |
| size = memparse(p, &p); | |
| if (*p == '@') | |
| start = memparse(p + 1, &p); | |
| system_ram->start = start; | |
| system_ram->end = system_ram->start + size - 1; | |
| return 0; | |
| } | |
| early_param("mem", early_mem); | |
| 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(struct tag *tag) | |
| { | |
| unsigned long start, end; | |
| /* | |
| * 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; | |
| start = tag->u.mem_range.addr; | |
| end = tag->u.mem_range.addr + tag->u.mem_range.size - 1; | |
| add_physical_memory(start, end); | |
| return 0; | |
| } | |
| __tagtable(ATAG_MEM, parse_tag_mem); | |
| static int __init parse_tag_rdimg(struct tag *tag) | |
| { | |
| #ifdef CONFIG_BLK_DEV_INITRD | |
| struct tag_mem_range *mem = &tag->u.mem_range; | |
| int ret; | |
| if (initrd_start) { | |
| printk(KERN_WARNING | |
| "Warning: Only the first initrd image will be used\n"); | |
| return 0; | |
| } | |
| ret = add_reserved_region(mem->addr, mem->addr + mem->size - 1, | |
| "initrd"); | |
| if (ret) { | |
| printk(KERN_WARNING | |
| "Warning: Failed to reserve initrd memory\n"); | |
| return ret; | |
| } | |
| initrd_start = (unsigned long)__va(mem->addr); | |
| initrd_end = initrd_start + mem->size; | |
| #else | |
| printk(KERN_WARNING "RAM disk image present, but " | |
| "no initrd support in kernel, ignoring\n"); | |
| #endif | |
| return 0; | |
| } | |
| __tagtable(ATAG_RDIMG, parse_tag_rdimg); | |
| static int __init parse_tag_rsvd_mem(struct tag *tag) | |
| { | |
| struct tag_mem_range *mem = &tag->u.mem_range; | |
| return add_reserved_region(mem->addr, mem->addr + mem->size - 1, | |
| "Reserved"); | |
| } | |
| __tagtable(ATAG_RSVD_MEM, parse_tag_rsvd_mem); | |
| static int __init parse_tag_cmdline(struct tag *tag) | |
| { | |
| strlcpy(boot_command_line, tag->u.cmdline.cmdline, COMMAND_LINE_SIZE); | |
| return 0; | |
| } | |
| __tagtable(ATAG_CMDLINE, parse_tag_cmdline); | |
| 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); | |
| /* | |
| * The board_number correspond to the bd->bi_board_number in U-Boot. This | |
| * parameter is only available during initialisation and can be used in some | |
| * kind of board identification. | |
| */ | |
| u32 __initdata board_number; | |
| static int __init parse_tag_boardinfo(struct tag *tag) | |
| { | |
| board_number = tag->u.boardinfo.board_number; | |
| return 0; | |
| } | |
| __tagtable(ATAG_BOARDINFO, parse_tag_boardinfo); | |
| /* | |
| * 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); | |
| } | |
| /* | |
| * Find a free memory region large enough for storing the | |
| * bootmem bitmap. | |
| */ | |
| static unsigned long __init | |
| find_bootmap_pfn(const struct resource *mem) | |
| { | |
| unsigned long bootmap_pages, bootmap_len; | |
| unsigned long node_pages = PFN_UP(resource_size(mem)); | |
| unsigned long bootmap_start; | |
| bootmap_pages = bootmem_bootmap_pages(node_pages); | |
| bootmap_len = bootmap_pages << PAGE_SHIFT; | |
| /* | |
| * Find a large enough region without reserved pages for | |
| * storing the bootmem bitmap. We can take advantage of the | |
| * fact that all lists have been sorted. | |
| * | |
| * We have to check that we don't collide with any reserved | |
| * regions, which includes the kernel image and any RAMDISK | |
| * images. | |
| */ | |
| bootmap_start = find_free_region(mem, bootmap_len, PAGE_SIZE); | |
| return bootmap_start >> PAGE_SHIFT; | |
| } | |
| #define MAX_LOWMEM HIGHMEM_START | |
| #define MAX_LOWMEM_PFN PFN_DOWN(MAX_LOWMEM) | |
| static void __init setup_bootmem(void) | |
| { | |
| unsigned bootmap_size; | |
| unsigned long first_pfn, bootmap_pfn, pages; | |
| unsigned long max_pfn, max_low_pfn; | |
| unsigned node = 0; | |
| struct resource *res; | |
| printk(KERN_INFO "Physical memory:\n"); | |
| for (res = system_ram; res; res = res->sibling) | |
| printk(" %08x-%08x\n", res->start, res->end); | |
| printk(KERN_INFO "Reserved memory:\n"); | |
| for (res = reserved; res; res = res->sibling) | |
| printk(" %08x-%08x: %s\n", | |
| res->start, res->end, res->name); | |
| nodes_clear(node_online_map); | |
| if (system_ram->sibling) | |
| printk(KERN_WARNING "Only using first memory bank\n"); | |
| for (res = system_ram; res; res = NULL) { | |
| first_pfn = PFN_UP(res->start); | |
| max_low_pfn = max_pfn = PFN_DOWN(res->end + 1); | |
| bootmap_pfn = find_bootmap_pfn(res); | |
| if (bootmap_pfn > max_pfn) | |
| panic("No space for bootmem bitmap!\n"); | |
| if (max_low_pfn > MAX_LOWMEM_PFN) { | |
| max_low_pfn = MAX_LOWMEM_PFN; | |
| #ifndef CONFIG_HIGHMEM | |
| /* | |
| * Lowmem is memory that can be addressed | |
| * directly through P1/P2 | |
| */ | |
| printk(KERN_WARNING | |
| "Node %u: Only %ld MiB of memory will be used.\n", | |
| node, MAX_LOWMEM >> 20); | |
| printk(KERN_WARNING "Use a HIGHMEM enabled kernel.\n"); | |
| #else | |
| #error HIGHMEM is not supported by AVR32 yet | |
| #endif | |
| } | |
| /* Initialize the boot-time allocator with low memory only. */ | |
| bootmap_size = init_bootmem_node(NODE_DATA(node), bootmap_pfn, | |
| first_pfn, max_low_pfn); | |
| /* | |
| * Register fully available RAM pages with the bootmem | |
| * allocator. | |
| */ | |
| pages = max_low_pfn - first_pfn; | |
| free_bootmem_node (NODE_DATA(node), PFN_PHYS(first_pfn), | |
| PFN_PHYS(pages)); | |
| /* Reserve space for the bootmem bitmap... */ | |
| reserve_bootmem_node(NODE_DATA(node), | |
| PFN_PHYS(bootmap_pfn), | |
| bootmap_size, | |
| BOOTMEM_DEFAULT); | |
| /* ...and any other reserved regions. */ | |
| for (res = reserved; res; res = res->sibling) { | |
| if (res->start > PFN_PHYS(max_pfn)) | |
| break; | |
| /* | |
| * resource_init will complain about partial | |
| * overlaps, so we'll just ignore such | |
| * resources for now. | |
| */ | |
| if (res->start >= PFN_PHYS(first_pfn) | |
| && res->end < PFN_PHYS(max_pfn)) | |
| reserve_bootmem_node(NODE_DATA(node), | |
| res->start, | |
| resource_size(res), | |
| BOOTMEM_DEFAULT); | |
| } | |
| node_set_online(node); | |
| } | |
| } | |
| void __init setup_arch (char **cmdline_p) | |
| { | |
| struct clk *cpu_clk; | |
| init_mm.start_code = (unsigned long)_stext; | |
| init_mm.end_code = (unsigned long)_etext; | |
| init_mm.end_data = (unsigned long)_edata; | |
| init_mm.brk = (unsigned long)_end; | |
| /* | |
| * Include .init section to make allocations easier. It will | |
| * be removed before the resource is actually requested. | |
| */ | |
| kernel_code.start = __pa(__init_begin); | |
| 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); | |
| 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); | |
| } | |
| strlcpy(command_line, boot_command_line, COMMAND_LINE_SIZE); | |
| *cmdline_p = command_line; | |
| parse_early_param(); | |
| setup_bootmem(); | |
| #ifdef CONFIG_VT | |
| conswitchp = &dummy_con; | |
| #endif | |
| paging_init(); | |
| resource_init(); | |
| } |