diff options
author | Stefan Reinauer <stefan.reinauer@coreboot.org> | 2012-12-07 17:15:04 -0800 |
---|---|---|
committer | Ronald G. Minnich <rminnich@gmail.com> | 2012-12-08 06:53:19 +0100 |
commit | 52db0b984523047da19ca3b41558b9dbf45abad7 (patch) | |
tree | 5ed389bb233d5b007593ede56040ccf268e37bbe /src/arch/armv7/lib/bootm.c | |
parent | 509f77277cfccdae897f0d369672ce0818ecdf88 (diff) | |
download | coreboot-52db0b984523047da19ca3b41558b9dbf45abad7.tar.xz |
WIP: Initial ARMv7 architecture implementation in coreboot
The first ARMv7 CPU we're going to support is the Exynos 5250
used in the Google Snow ChromeBook.
Change-Id: I4de8433bbc6202eb8fef2556a11186a3376d411b
Signed-off-by: David Hendricks <dhendrix@chromium.org>
Signed-off-by: Stefan Reinauer <reinauer@google.com>
Signed-off-by: Ronald G. Minnich <rminnich@gmail.com>
Reviewed-on: http://review.coreboot.org/2004
Tested-by: build bot (Jenkins)
Diffstat (limited to 'src/arch/armv7/lib/bootm.c')
-rw-r--r-- | src/arch/armv7/lib/bootm.c | 405 |
1 files changed, 405 insertions, 0 deletions
diff --git a/src/arch/armv7/lib/bootm.c b/src/arch/armv7/lib/bootm.c new file mode 100644 index 0000000000..afe1b70814 --- /dev/null +++ b/src/arch/armv7/lib/bootm.c @@ -0,0 +1,405 @@ +/* + * (C) Copyright 2002 + * Sysgo Real-Time Solutions, GmbH <www.elinos.com> + * Marius Groeger <mgroeger@sysgo.de> + * + * Copyright (C) 2001 Erik Mouw (J.A.K.Mouw@its.tudelft.nl) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <common.h> +#include <command.h> +#include <image.h> +#include <u-boot/zlib.h> +#include <arch/byteorder.h> +#include <fdt.h> +#include <libfdt.h> +#include <fdt_support.h> + +DECLARE_GLOBAL_DATA_PTR; + +#if defined (CONFIG_SETUP_MEMORY_TAGS) || \ + defined (CONFIG_CMDLINE_TAG) || \ + defined (CONFIG_INITRD_TAG) || \ + defined (CONFIG_SERIAL_TAG) || \ + defined (CONFIG_REVISION_TAG) +static void setup_start_tag (bd_t *bd); + +# ifdef CONFIG_SETUP_MEMORY_TAGS +static void setup_memory_tags (bd_t *bd); +# endif +static void setup_commandline_tag (bd_t *bd, char *commandline); + +# ifdef CONFIG_INITRD_TAG +static void setup_initrd_tag (bd_t *bd, ulong initrd_start, + ulong initrd_end); +# endif +static void setup_end_tag (bd_t *bd); + +# if defined (CONFIG_VFD) || defined (CONFIG_LCD) +# if !defined(CONFIG_VIDEOFLB_ATAG_NOT_SUPPORTED) +static void setup_videolfb_tag (gd_t *gd); +# endif +# endif + +static struct tag *params; +#endif /* CONFIG_SETUP_MEMORY_TAGS || CONFIG_CMDLINE_TAG || CONFIG_INITRD_TAG */ + +static ulong get_sp(void); +#if defined(CONFIG_OF_LIBFDT) && !defined(CONFIG_OF_NO_KERNEL) +static int bootm_linux_fdt(int machid, bootm_headers_t *images); +#endif + +void arch_lmb_reserve(struct lmb *lmb) +{ + ulong sp; + + /* + * Booting a (Linux) kernel image + * + * Allocate space for command line and board info - the + * address should be as high as possible within the reach of + * the kernel (see CONFIG_SYS_BOOTMAPSZ settings), but in unused + * memory, which means far enough below the current stack + * pointer. + */ + sp = get_sp(); + debug("## Current stack ends at 0x%08lx ", sp); + + /* adjust sp by 1K to be safe */ + sp -= 1024; + lmb_reserve(lmb, sp, + gd->bd->bi_dram[0].start + gd->bd->bi_dram[0].size - sp); +} + +static void announce_and_cleanup(void) +{ + printf("\nStarting kernel ...\n\n"); + bootstage_mark_name(BOOTSTAGE_ID_BOOTM_HANDOFF, "start_kernel"); + +#ifdef CONFIG_USB_DEVICE + { + extern void udc_disconnect(void); + udc_disconnect(); + } +#endif + cleanup_before_linux(); +} + +int do_bootm_linux(int flag, int argc, char *argv[], bootm_headers_t *images) +{ + bd_t *bd = gd->bd; + char *s; + int machid = bd->bi_arch_number; + void (*kernel_entry)(int zero, int arch, uint params); + +#ifdef CONFIG_CMDLINE_TAG + char *commandline = getenv ("bootargs"); +#endif + + if ((flag != 0) && (flag != BOOTM_STATE_OS_GO)) + return 1; + + s = getenv ("machid"); + if (s) { + machid = simple_strtoul (s, NULL, 16); + printf ("Using machid 0x%x from environment\n", machid); + } + + bootstage_mark(BOOTSTAGE_ID_RUN_OS); + +#if defined(CONFIG_OF_LIBFDT) && !defined(CONFIG_OF_NO_KERNEL) + if (images->ft_len) + return bootm_linux_fdt(machid, images); +#endif + + kernel_entry = (void (*)(int, int, uint))images->ep; + + debug ("## Transferring control to Linux (at address %08lx) ...\n", + (ulong) kernel_entry); + +#if defined (CONFIG_SETUP_MEMORY_TAGS) || \ + defined (CONFIG_CMDLINE_TAG) || \ + defined (CONFIG_INITRD_TAG) || \ + defined (CONFIG_SERIAL_TAG) || \ + defined (CONFIG_REVISION_TAG) + setup_start_tag (bd); +#ifdef CONFIG_SERIAL_TAG + setup_serial_tag (¶ms); +#endif +#ifdef CONFIG_REVISION_TAG + setup_revision_tag (¶ms); +#endif +#ifdef CONFIG_SETUP_MEMORY_TAGS + setup_memory_tags (bd); +#endif +#ifdef CONFIG_CMDLINE_TAG + setup_commandline_tag (bd, commandline); +#endif +#ifdef CONFIG_INITRD_TAG + if (images->rd_start && images->rd_end) + setup_initrd_tag (bd, images->rd_start, images->rd_end); +#endif +#if defined (CONFIG_VFD) || defined (CONFIG_LCD) +#if !defined(CONFIG_VIDEOFLB_ATAG_NOT_SUPPORTED) + setup_videolfb_tag ((gd_t *) gd); +#endif +#endif + setup_end_tag (bd); +#endif + + announce_and_cleanup(); + + kernel_entry(0, machid, bd->bi_boot_params); + /* does not return */ + + return 1; +} + +#if defined(CONFIG_OF_LIBFDT) && !defined(CONFIG_OF_NO_KERNEL) +static int fixup_memory_node(void *blob) +{ + bd_t *bd = gd->bd; + int bank; + u64 start[CONFIG_NR_DRAM_BANKS]; + u64 size[CONFIG_NR_DRAM_BANKS]; + + for (bank = 0; bank < CONFIG_NR_DRAM_BANKS; bank++) { + start[bank] = bd->bi_dram[bank].start; + size[bank] = bd->bi_dram[bank].size; + } + + return fdt_fixup_memory_banks(blob, start, size, CONFIG_NR_DRAM_BANKS); +} + +static int bootm_linux_fdt(int machid, bootm_headers_t *images) +{ + ulong rd_len; + void (*kernel_entry)(int zero, int dt_machid, void *dtblob); + ulong of_size = images->ft_len; + char **of_flat_tree = &images->ft_addr; + ulong *initrd_start = &images->initrd_start; + ulong *initrd_end = &images->initrd_end; + struct lmb *lmb = &images->lmb; + int ret; + + kernel_entry = (void (*)(int, int, void *))images->ep; + + boot_fdt_add_mem_rsv_regions(lmb, *of_flat_tree); + + rd_len = images->rd_end - images->rd_start; + ret = boot_ramdisk_high(lmb, images->rd_start, rd_len, + initrd_start, initrd_end); + if (ret) + return ret; + +#ifdef CONFIG_OF_BOARD_SETUP + /* Try to reserve 1024 bytes for board fixups */ + if (!fdt_open_into(*of_flat_tree, *of_flat_tree, of_size + 1024)) + of_size += 1024; + /* Call the board-specific fixup routine */ + ft_board_setup(*of_flat_tree, gd->bd); +#endif +#ifdef CONFIG_OF_UPDATE_FDT_BEFORE_BOOT + /* this must be earlier than boot_relocate_fdt */ + ret = fit_update_fdt_before_boot(*of_flat_tree, &of_size); + if (ret) + return ret; +#endif + + ret = boot_relocate_fdt(lmb, of_flat_tree, &of_size); + if (ret) + return ret; + + debug("## Transferring control to Linux (at address %08lx) ...\n", + (ulong) kernel_entry); + + fdt_chosen(*of_flat_tree, 1); + + fixup_memory_node(*of_flat_tree); + + fdt_fixup_ethernet(*of_flat_tree); + + fdt_initrd(*of_flat_tree, *initrd_start, *initrd_end, 1); + + announce_and_cleanup(); + + kernel_entry(0, machid, *of_flat_tree); + /* does not return */ + + return 1; +} +#endif + +#if defined (CONFIG_SETUP_MEMORY_TAGS) || \ + defined (CONFIG_CMDLINE_TAG) || \ + defined (CONFIG_INITRD_TAG) || \ + defined (CONFIG_SERIAL_TAG) || \ + defined (CONFIG_REVISION_TAG) +static void setup_start_tag (bd_t *bd) +{ + params = (struct tag *) bd->bi_boot_params; + + params->hdr.tag = ATAG_CORE; + params->hdr.size = tag_size (tag_core); + +#if defined (ATAG_CORE_FLAGS) && \ + defined (ATAG_PAGE_SIZE) && \ + defined (ATAG_CORE_RDEV) + params->u.core.flags = ATAG_CORE_FLAGS; + params->u.core.pagesize = ATAG_PAGE_SIZE; + params->u.core.rootdev = ATAG_CORE_RDEV; +#else + params->u.core.flags = 0; + params->u.core.pagesize = 0; + params->u.core.rootdev = 0; +#endif + + params = tag_next (params); +} + + +#ifdef CONFIG_SETUP_MEMORY_TAGS +static void setup_memory_tags (bd_t *bd) +{ + int i; + + for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { + params->hdr.tag = ATAG_MEM; + params->hdr.size = tag_size (tag_mem32); + + params->u.mem.start = bd->bi_dram[i].start; + params->u.mem.size = bd->bi_dram[i].size; + + params = tag_next (params); + } +} +#endif /* CONFIG_SETUP_MEMORY_TAGS */ + + +static void setup_commandline_tag (bd_t *bd, char *commandline) +{ + char *p; + + if (!commandline) + return; + + /* eat leading white space */ + for (p = commandline; *p == ' '; p++); + + /* skip non-existent command lines so the kernel will still + * use its default command line. + */ + if (*p == '\0') + return; + + params->hdr.tag = ATAG_CMDLINE; + params->hdr.size = + (sizeof (struct tag_header) + strlen (p) + 1 + 4) >> 2; + + strcpy (params->u.cmdline.cmdline, p); + + params = tag_next (params); +} + + +#ifdef CONFIG_INITRD_TAG +static void setup_initrd_tag (bd_t *bd, ulong initrd_start, ulong initrd_end) +{ + /* an ATAG_INITRD node tells the kernel where the compressed + * ramdisk can be found. ATAG_RDIMG is a better name, actually. + */ + params->hdr.tag = ATAG_INITRD2; + params->hdr.size = tag_size (tag_initrd); + + params->u.initrd.start = initrd_start; + params->u.initrd.size = initrd_end - initrd_start; + + params = tag_next (params); +} +#endif /* CONFIG_INITRD_TAG */ + +#if defined (CONFIG_VFD) || defined (CONFIG_LCD) +#if !defined(CONFIG_VIDEOFLB_ATAG_NOT_SUPPORTED) +extern ulong calc_fbsize (void); +static void setup_videolfb_tag (gd_t *gd) +{ + /* An ATAG_VIDEOLFB node tells the kernel where and how large + * the framebuffer for video was allocated (among other things). + * Note that a _physical_ address is passed ! + * + * We only use it to pass the address and size, the other entries + * in the tag_videolfb are not of interest. + */ + params->hdr.tag = ATAG_VIDEOLFB; + params->hdr.size = tag_size (tag_videolfb); + + params->u.videolfb.lfb_base = (u32) gd->fb_base; + /* Fb size is calculated according to parameters for our panel + */ + params->u.videolfb.lfb_size = calc_fbsize(); + + params = tag_next (params); +} +#endif /* CONFIG_VIDEOFLB_ATAG_NOT_SUPPORTED */ +#endif /* CONFIG_VFD || CONFIG_LCD */ + +#ifdef CONFIG_SERIAL_TAG +void setup_serial_tag (struct tag **tmp) +{ + struct tag *params = *tmp; + struct tag_serialnr serialnr; + void get_board_serial(struct tag_serialnr *serialnr); + + get_board_serial(&serialnr); + params->hdr.tag = ATAG_SERIAL; + params->hdr.size = tag_size (tag_serialnr); + params->u.serialnr.low = serialnr.low; + params->u.serialnr.high= serialnr.high; + params = tag_next (params); + *tmp = params; +} +#endif + +#ifdef CONFIG_REVISION_TAG +void setup_revision_tag(struct tag **in_params) +{ + u32 rev = 0; + u32 get_board_rev(void); + + rev = get_board_rev(); + params->hdr.tag = ATAG_REVISION; + params->hdr.size = tag_size (tag_revision); + params->u.revision.rev = rev; + params = tag_next (params); +} +#endif /* CONFIG_REVISION_TAG */ + +static void setup_end_tag (bd_t *bd) +{ + params->hdr.tag = ATAG_NONE; + params->hdr.size = 0; +} +#endif /* CONFIG_SETUP_MEMORY_TAGS || CONFIG_CMDLINE_TAG || CONFIG_INITRD_TAG */ + +static ulong get_sp(void) +{ + ulong ret; + + asm("mov %0, sp" : "=r"(ret) : ); + return ret; +} |