Wolfgang Denk | 7b64fef | 2006-10-24 14:21:16 +0200 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2004-2006 Atmel Corporation |
| 3 | * |
Wolfgang Denk | 1a45966 | 2013-07-08 09:37:19 +0200 | [diff] [blame] | 4 | * SPDX-License-Identifier: GPL-2.0+ |
Wolfgang Denk | 7b64fef | 2006-10-24 14:21:16 +0200 | [diff] [blame] | 5 | */ |
| 6 | #include <common.h> |
| 7 | #include <command.h> |
| 8 | #include <image.h> |
Jean-Christophe PLAGNIOL-VILLARD | a31e091 | 2009-04-04 12:49:11 +0200 | [diff] [blame] | 9 | #include <u-boot/zlib.h> |
Wolfgang Denk | 7b64fef | 2006-10-24 14:21:16 +0200 | [diff] [blame] | 10 | #include <asm/byteorder.h> |
Olav Morken | a38de08 | 2009-01-23 12:56:28 +0100 | [diff] [blame] | 11 | #include <asm/arch/addrspace.h> |
Wolfgang Denk | 7b64fef | 2006-10-24 14:21:16 +0200 | [diff] [blame] | 12 | #include <asm/io.h> |
| 13 | #include <asm/setup.h> |
Haavard Skinnemoen | df548d3 | 2006-11-19 18:06:53 +0100 | [diff] [blame] | 14 | #include <asm/arch/clk.h> |
Wolfgang Denk | 7b64fef | 2006-10-24 14:21:16 +0200 | [diff] [blame] | 15 | |
| 16 | DECLARE_GLOBAL_DATA_PTR; |
| 17 | |
Wolfgang Denk | 7b64fef | 2006-10-24 14:21:16 +0200 | [diff] [blame] | 18 | /* CPU-specific hook to allow flushing of caches, etc. */ |
| 19 | extern void prepare_to_boot(void); |
| 20 | |
Wolfgang Denk | 7b64fef | 2006-10-24 14:21:16 +0200 | [diff] [blame] | 21 | static struct tag *setup_start_tag(struct tag *params) |
| 22 | { |
| 23 | params->hdr.tag = ATAG_CORE; |
| 24 | params->hdr.size = tag_size(tag_core); |
| 25 | |
| 26 | params->u.core.flags = 0; |
| 27 | params->u.core.pagesize = 4096; |
| 28 | params->u.core.rootdev = 0; |
| 29 | |
| 30 | return tag_next(params); |
| 31 | } |
| 32 | |
| 33 | static struct tag *setup_memory_tags(struct tag *params) |
| 34 | { |
| 35 | bd_t *bd = gd->bd; |
| 36 | int i; |
| 37 | |
| 38 | for (i = 0; i < CONFIG_NR_DRAM_BANKS; i++) { |
| 39 | params->hdr.tag = ATAG_MEM; |
| 40 | params->hdr.size = tag_size(tag_mem_range); |
| 41 | |
| 42 | params->u.mem_range.addr = bd->bi_dram[i].start; |
| 43 | params->u.mem_range.size = bd->bi_dram[i].size; |
| 44 | |
| 45 | params = tag_next(params); |
| 46 | } |
| 47 | |
| 48 | return params; |
| 49 | } |
| 50 | |
| 51 | static struct tag *setup_commandline_tag(struct tag *params, char *cmdline) |
| 52 | { |
| 53 | if (!cmdline) |
| 54 | return params; |
| 55 | |
| 56 | /* eat leading white space */ |
| 57 | while (*cmdline == ' ') cmdline++; |
| 58 | |
| 59 | /* |
| 60 | * Don't include tags for empty command lines; let the kernel |
| 61 | * use its default command line. |
| 62 | */ |
| 63 | if (*cmdline == '\0') |
| 64 | return params; |
| 65 | |
| 66 | params->hdr.tag = ATAG_CMDLINE; |
| 67 | params->hdr.size = |
| 68 | (sizeof (struct tag_header) + strlen(cmdline) + 1 + 3) >> 2; |
| 69 | strcpy(params->u.cmdline.cmdline, cmdline); |
| 70 | |
| 71 | return tag_next(params); |
| 72 | } |
| 73 | |
| 74 | static struct tag *setup_ramdisk_tag(struct tag *params, |
| 75 | unsigned long rd_start, |
| 76 | unsigned long rd_end) |
| 77 | { |
| 78 | if (rd_start == rd_end) |
| 79 | return params; |
| 80 | |
| 81 | params->hdr.tag = ATAG_RDIMG; |
| 82 | params->hdr.size = tag_size(tag_mem_range); |
| 83 | |
| 84 | params->u.mem_range.addr = rd_start; |
| 85 | params->u.mem_range.size = rd_end - rd_start; |
| 86 | |
| 87 | return tag_next(params); |
| 88 | } |
| 89 | |
| 90 | static struct tag *setup_clock_tags(struct tag *params) |
| 91 | { |
| 92 | params->hdr.tag = ATAG_CLOCK; |
| 93 | params->hdr.size = tag_size(tag_clock); |
| 94 | params->u.clock.clock_id = ACLOCK_BOOTCPU; |
| 95 | params->u.clock.clock_flags = 0; |
Simon Glass | 3d0f8c8 | 2012-12-13 20:49:10 +0000 | [diff] [blame] | 96 | params->u.clock.clock_hz = gd->arch.cpu_hz; |
Wolfgang Denk | 7b64fef | 2006-10-24 14:21:16 +0200 | [diff] [blame] | 97 | |
| 98 | #ifdef CONFIG_AT32AP7000 |
| 99 | /* |
| 100 | * New kernels don't need this, but we should be backwards |
| 101 | * compatible for a while... |
| 102 | */ |
| 103 | params = tag_next(params); |
| 104 | |
| 105 | params->hdr.tag = ATAG_CLOCK; |
| 106 | params->hdr.size = tag_size(tag_clock); |
| 107 | params->u.clock.clock_id = ACLOCK_HSB; |
| 108 | params->u.clock.clock_flags = 0; |
Haavard Skinnemoen | df548d3 | 2006-11-19 18:06:53 +0100 | [diff] [blame] | 109 | params->u.clock.clock_hz = get_hsb_clk_rate(); |
Wolfgang Denk | 7b64fef | 2006-10-24 14:21:16 +0200 | [diff] [blame] | 110 | #endif |
| 111 | |
| 112 | return tag_next(params); |
| 113 | } |
| 114 | |
| 115 | static struct tag *setup_ethernet_tag(struct tag *params, |
| 116 | char *addr, int index) |
| 117 | { |
| 118 | char *s, *e; |
| 119 | int i; |
| 120 | |
| 121 | params->hdr.tag = ATAG_ETHERNET; |
| 122 | params->hdr.size = tag_size(tag_ethernet); |
| 123 | |
| 124 | params->u.ethernet.mac_index = index; |
| 125 | params->u.ethernet.mii_phy_addr = gd->bd->bi_phy_id[index]; |
| 126 | |
| 127 | s = addr; |
| 128 | for (i = 0; i < 6; i++) { |
| 129 | params->u.ethernet.hw_address[i] = simple_strtoul(s, &e, 16); |
| 130 | s = e + 1; |
| 131 | } |
| 132 | |
| 133 | return tag_next(params); |
| 134 | } |
| 135 | |
| 136 | static struct tag *setup_ethernet_tags(struct tag *params) |
| 137 | { |
| 138 | char name[16] = "ethaddr"; |
| 139 | char *addr; |
| 140 | int i = 0; |
| 141 | |
| 142 | do { |
| 143 | addr = getenv(name); |
| 144 | if (addr) |
| 145 | params = setup_ethernet_tag(params, addr, i); |
| 146 | sprintf(name, "eth%daddr", ++i); |
| 147 | } while (i < 4); |
| 148 | |
| 149 | return params; |
| 150 | } |
| 151 | |
Andreas Bießmann | 24890f1 | 2011-04-12 23:25:41 +0000 | [diff] [blame] | 152 | static struct tag *setup_boardinfo_tag(struct tag *params) |
| 153 | { |
| 154 | params->hdr.tag = ATAG_BOARDINFO; |
| 155 | params->hdr.size = tag_size(tag_boardinfo); |
| 156 | |
| 157 | params->u.boardinfo.board_number = gd->bd->bi_board_number; |
| 158 | |
| 159 | return tag_next(params); |
| 160 | } |
| 161 | |
Wolfgang Denk | 7b64fef | 2006-10-24 14:21:16 +0200 | [diff] [blame] | 162 | static void setup_end_tag(struct tag *params) |
| 163 | { |
| 164 | params->hdr.tag = ATAG_NONE; |
| 165 | params->hdr.size = 0; |
| 166 | } |
| 167 | |
Wolfgang Denk | 54841ab | 2010-06-28 22:00:46 +0200 | [diff] [blame] | 168 | int do_bootm_linux(int flag, int argc, char * const argv[], bootm_headers_t *images) |
Wolfgang Denk | 7b64fef | 2006-10-24 14:21:16 +0200 | [diff] [blame] | 169 | { |
Marian Balakowicz | d5934ad | 2008-02-04 08:28:09 +0100 | [diff] [blame] | 170 | void (*theKernel)(int magic, void *tagtable); |
| 171 | struct tag *params, *params_start; |
| 172 | char *commandline = getenv("bootargs"); |
Marian Balakowicz | f13e7b2 | 2008-01-08 18:12:17 +0100 | [diff] [blame] | 173 | |
Andreas Bießmann | 2cb0e55 | 2013-07-02 13:57:44 +0200 | [diff] [blame] | 174 | /* |
| 175 | * allow the PREP bootm subcommand, it is required for bootm to work |
| 176 | * |
Andreas Bießmann | 09c2b8f | 2016-05-01 03:46:16 +0200 | [diff] [blame] | 177 | * TODO: Andreas Bießmann <andreas@biessmann.org> refactor the |
Andreas Bießmann | 2cb0e55 | 2013-07-02 13:57:44 +0200 | [diff] [blame] | 178 | * do_bootm_linux() for avr32 |
| 179 | */ |
| 180 | if (flag & BOOTM_STATE_OS_PREP) |
| 181 | return 0; |
| 182 | |
Kumar Gala | 49c3a86 | 2008-10-21 17:25:45 -0500 | [diff] [blame] | 183 | if ((flag != 0) && (flag != BOOTM_STATE_OS_GO)) |
| 184 | return 1; |
| 185 | |
Kumar Gala | c160a95 | 2008-08-15 08:24:36 -0500 | [diff] [blame] | 186 | theKernel = (void *)images->ep; |
Wolfgang Denk | 7b64fef | 2006-10-24 14:21:16 +0200 | [diff] [blame] | 187 | |
Simon Glass | 770605e | 2012-02-13 13:51:18 +0000 | [diff] [blame] | 188 | bootstage_mark(BOOTSTAGE_ID_RUN_OS); |
Wolfgang Denk | 7b64fef | 2006-10-24 14:21:16 +0200 | [diff] [blame] | 189 | |
| 190 | params = params_start = (struct tag *)gd->bd->bi_boot_params; |
| 191 | params = setup_start_tag(params); |
| 192 | params = setup_memory_tags(params); |
Kumar Gala | c4f9419 | 2008-08-15 08:24:37 -0500 | [diff] [blame] | 193 | if (images->rd_start) { |
Wolfgang Denk | 7b64fef | 2006-10-24 14:21:16 +0200 | [diff] [blame] | 194 | params = setup_ramdisk_tag(params, |
Kumar Gala | c4f9419 | 2008-08-15 08:24:37 -0500 | [diff] [blame] | 195 | PHYSADDR(images->rd_start), |
| 196 | PHYSADDR(images->rd_end)); |
Wolfgang Denk | 7b64fef | 2006-10-24 14:21:16 +0200 | [diff] [blame] | 197 | } |
| 198 | params = setup_commandline_tag(params, commandline); |
| 199 | params = setup_clock_tags(params); |
| 200 | params = setup_ethernet_tags(params); |
Andreas Bießmann | 24890f1 | 2011-04-12 23:25:41 +0000 | [diff] [blame] | 201 | params = setup_boardinfo_tag(params); |
Wolfgang Denk | 7b64fef | 2006-10-24 14:21:16 +0200 | [diff] [blame] | 202 | setup_end_tag(params); |
| 203 | |
Wolfgang Denk | 7b64fef | 2006-10-24 14:21:16 +0200 | [diff] [blame] | 204 | printf("\nStarting kernel at %p (params at %p)...\n\n", |
| 205 | theKernel, params_start); |
| 206 | |
| 207 | prepare_to_boot(); |
| 208 | |
| 209 | theKernel(ATAG_MAGIC, params_start); |
Marian Balakowicz | cd7c596 | 2008-03-12 10:33:00 +0100 | [diff] [blame] | 210 | /* does not return */ |
Jean-Christophe PLAGNIOL-VILLARD | a3a08c0 | 2008-09-10 22:48:09 +0200 | [diff] [blame] | 211 | |
Kumar Gala | 40d7e99 | 2008-08-15 08:24:45 -0500 | [diff] [blame] | 212 | return 1; |
Wolfgang Denk | 7b64fef | 2006-10-24 14:21:16 +0200 | [diff] [blame] | 213 | } |