| /* |
| * 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(); |
| } |