Fast boot from flash
[mw/milkymist.git] / software / bios / main.c
1 /*
2  * Milkymist VJ SoC (Software)
3  * Copyright (C) 2007, 2008, 2009, 2010 Sebastien Bourdeauducq
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, version 3 of the License.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 #include <stdio.h>
19 #include <console.h>
20 #include <string.h>
21 #include <uart.h>
22 #include <blockdev.h>
23 #include <fatfs.h>
24 #include <crc.h>
25 #include <system.h>
26 #include <board.h>
27 #include <version.h>
28 #include <net/mdio.h>
29 #include <hw/vga.h>
30 #include <hw/fmlbrg.h>
31 #include <hw/sysctl.h>
32 #include <hw/capabilities.h>
33 #include <hw/gpio.h>
34 #include <hw/uart.h>
35 #include <hw/hpdmc.h>
36 #include <hw/flash.h>
37
38 #include "boot.h"
39 #include "splash.h"
40
41 const struct board_desc *brd_desc;
42
43 /* General address space functions */
44
45 #define NUMBER_OF_BYTES_ON_A_LINE 16
46 static void dump_bytes(unsigned int *ptr, int count, unsigned addr)
47 {
48         char *data = (char *)ptr;
49         int line_bytes = 0, i = 0;
50
51         putsnonl("Memory dump:");
52         while(count > 0){
53                 line_bytes =
54                         (count > NUMBER_OF_BYTES_ON_A_LINE)?
55                                 NUMBER_OF_BYTES_ON_A_LINE : count;
56
57                 printf("\n0x%08x  ", addr);
58                 for(i=0;i<line_bytes;i++)
59                         printf("%02x ", *(unsigned char *)(data+i));
60         
61                 for(;i<NUMBER_OF_BYTES_ON_A_LINE;i++)
62                         printf("   ");
63         
64                 printf(" ");
65
66                 for(i=0;i<line_bytes;i++) {
67                         if((*(data+i) < 0x20) || (*(data+i) > 0x7e))
68                                 printf(".");
69                         else
70                                 printf("%c", *(data+i));
71                 }       
72
73                 for(;i<NUMBER_OF_BYTES_ON_A_LINE;i++)
74                         printf(" ");
75
76                 data += (char)line_bytes;
77                 count -= line_bytes;
78                 addr += line_bytes;
79         }
80         printf("\n");
81 }
82
83
84 static void mr(char *startaddr, char *len)
85 {
86         char *c;
87         unsigned int *addr;
88         unsigned int length;
89
90         if(*startaddr == 0) {
91                 printf("mr <address> [length]\n");
92                 return;
93         }
94         addr = (unsigned *)strtoul(startaddr, &c, 0);
95         if(*c != 0) {
96                 printf("incorrect address\n");
97                 return;
98         }
99         if(*len == 0) {
100                 length = 1;
101         } else {
102                 length = strtoul(len, &c, 0);
103                 if(*c != 0) {
104                         printf("incorrect length\n");
105                         return;
106                 }
107         }
108
109         dump_bytes(addr, length, (unsigned)addr);
110 }
111
112 static void mw(char *addr, char *value, char *count)
113 {
114         char *c;
115         unsigned int *addr2;
116         unsigned int value2;
117         unsigned int count2;
118         unsigned int i;
119
120         if((*addr == 0) || (*value == 0)) {
121                 printf("mw <address> <value> [count]\n");
122                 return;
123         }
124         addr2 = (unsigned int *)strtoul(addr, &c, 0);
125         if(*c != 0) {
126                 printf("incorrect address\n");
127                 return;
128         }
129         value2 = strtoul(value, &c, 0);
130         if(*c != 0) {
131                 printf("incorrect value\n");
132                 return;
133         }
134         if(*count == 0) {
135                 count2 = 1;
136         } else {
137                 count2 = strtoul(count, &c, 0);
138                 if(*c != 0) {
139                         printf("incorrect count\n");
140                         return;
141                 }
142         }
143         for (i=0;i<count2;i++) *addr2++ = value2;
144 }
145
146 static void mc(char *dstaddr, char *srcaddr, char *count)
147 {
148         char *c;
149         unsigned int *dstaddr2;
150         unsigned int *srcaddr2;
151         unsigned int count2;
152         unsigned int i;
153
154         if((*dstaddr == 0) || (*srcaddr == 0)) {
155                 printf("mc <dst> <src> [count]\n");
156                 return;
157         }
158         dstaddr2 = (unsigned int *)strtoul(dstaddr, &c, 0);
159         if(*c != 0) {
160                 printf("incorrect destination address\n");
161                 return;
162         }
163         srcaddr2 = (unsigned int *)strtoul(srcaddr, &c, 0);
164         if(*c != 0) {
165                 printf("incorrect source address\n");
166                 return;
167         }
168         if(*count == 0) {
169                 count2 = 1;
170         } else {
171                 count2 = strtoul(count, &c, 0);
172                 if(*c != 0) {
173                         printf("incorrect count\n");
174                         return;
175                 }
176         }
177         for (i=0;i<count2;i++) *dstaddr2++ = *srcaddr2++;
178 }
179
180 static void crc(char *startaddr, char *len)
181 {
182         char *c;
183         char *addr;
184         unsigned int length;
185
186         if((*startaddr == 0)||(*len == 0)) {
187                 printf("crc <address> <length>\n");
188                 return;
189         }
190         addr = (char *)strtoul(startaddr, &c, 0);
191         if(*c != 0) {
192                 printf("incorrect address\n");
193                 return;
194         }
195         length = strtoul(len, &c, 0);
196         if(*c != 0) {
197                 printf("incorrect length\n");
198                 return;
199         }
200
201         printf("CRC32: %08x\n", crc32((unsigned char *)addr, length));
202 }
203
204 /* CF filesystem functions */
205
206 static int lscb(const char *filename, const char *longname, void *param)
207 {
208         printf("%12s [%s]\n", filename, longname);
209         return 1;
210 }
211
212 static void ls()
213 {
214         fatfs_init(BLOCKDEV_FLASH, 0);
215         fatfs_list_files(lscb, NULL);
216         fatfs_done();
217 }
218
219 static void load(char *filename, char *addr)
220 {
221         char *c;
222         unsigned int *addr2;
223
224         if((*filename == 0) || (*addr == 0)) {
225                 printf("load <filename> <address>\n");
226                 return;
227         }
228         addr2 = (unsigned *)strtoul(addr, &c, 0);
229         if(*c != 0) {
230                 printf("incorrect address\n");
231                 return;
232         }
233         fatfs_init(BLOCKDEV_FLASH, 0);
234         fatfs_load(filename, (char *)addr2, 16*1024*1024, NULL);
235         fatfs_done();
236 }
237
238 static void mdior(char *reg)
239 {
240         char *c;
241         int reg2;
242
243         if(*reg == 0) {
244                 printf("mdior <register>\n");
245                 return;
246         }
247         reg2 = strtoul(reg, &c, 0);
248         if(*c != 0) {
249                 printf("incorrect register\n");
250                 return;
251         }
252
253         printf("%04x\n", mdio_read(brd_desc->ethernet_phyadr, reg2));
254 }
255
256 static void mdiow(char *reg, char *value)
257 {
258         char *c;
259         int reg2;
260         int value2;
261
262         if((*reg == 0) || (*value == 0)) {
263                 printf("mdiow <register> <value>\n");
264                 return;
265         }
266         reg2 = strtoul(reg, &c, 0);
267         if(*c != 0) {
268                 printf("incorrect address\n");
269                 return;
270         }
271         value2 = strtoul(value, &c, 0);
272         if(*c != 0) {
273                 printf("incorrect value\n");
274                 return;
275         }
276
277         mdio_write(brd_desc->ethernet_phyadr, reg2, value2);
278 }
279
280 /* Init + command line */
281
282 static void help()
283 {
284         puts("This is the Milkymist BIOS debug shell.");
285         puts("It is used for system development and maintainance, and not");
286         puts("for regular operation.\n");
287         puts("Available commands:");
288         puts("flush      - flush FML bridge cache");
289         puts("mr         - read address space");
290         puts("mw         - write address space");
291         puts("mc         - copy address space");
292         puts("crc        - compute CRC32 of a part of the address space");
293         puts("ls         - list files on the filesystem");
294         puts("load       - load a file from the filesystem");
295         puts("serialboot - boot via SFL");
296         puts("netboot    - boot via TFTP");
297         puts("fsboot     - boot from the filesystem");
298         puts("mdior      - read MDIO register");
299         puts("mdiow      - write MDIO register");
300         puts("reboot     - system reset");
301 }
302
303 static char *get_token(char **str)
304 {
305         char *c, *d;
306
307         c = (char *)strchr(*str, ' ');
308         if(c == NULL) {
309                 d = *str;
310                 *str = *str+strlen(*str);
311                 return d;
312         }
313         *c = 0;
314         d = *str;
315         *str = c+1;
316         return d;
317 }
318
319 static void do_command(char *c)
320 {
321         char *token;
322
323         token = get_token(&c);
324
325         if(strcmp(token, "flush") == 0) flush_bridge_cache();
326
327         else if(strcmp(token, "mr") == 0) mr(get_token(&c), get_token(&c));
328         else if(strcmp(token, "mw") == 0) mw(get_token(&c), get_token(&c), get_token(&c));
329         else if(strcmp(token, "mc") == 0) mc(get_token(&c), get_token(&c), get_token(&c));
330         else if(strcmp(token, "crc") == 0) crc(get_token(&c), get_token(&c));
331         
332         else if(strcmp(token, "ls") == 0) ls();
333         else if(strcmp(token, "load") == 0) load(get_token(&c), get_token(&c));
334         
335         else if(strcmp(token, "serialboot") == 0) serialboot();
336         else if(strcmp(token, "netboot") == 0) netboot();
337         else if(strcmp(token, "fsboot") == 0) fsboot();
338
339         else if(strcmp(token, "mdior") == 0) mdior(get_token(&c));
340         else if(strcmp(token, "mdiow") == 0) mdiow(get_token(&c), get_token(&c));
341
342         else if(strcmp(token, "reboot") == 0) reboot();
343         
344         else if(strcmp(token, "help") == 0) help();
345         
346         else if(strcmp(token, "") != 0)
347                 printf("Command not found\n");
348 }
349
350 static int test_user_abort()
351 {
352         unsigned int i;
353         char c;
354         
355         puts("I: Press Q to abort boot");
356         for(i=0;i<3500000;i++) {
357                 if(readchar_nonblock()) {
358                         c = readchar();
359                         if(c == 'Q') {
360                                 puts("I: Aborted boot on user request");
361                                 return 0;
362                         }
363                 }
364         }
365         return 1;
366 }
367
368 extern unsigned int _edata;
369
370 static void crcbios()
371 {
372         unsigned int length;
373         unsigned int expected_crc;
374         unsigned int actual_crc;
375         
376         /*
377          * _edata is located right after the end of the flat
378          * binary image. The CRC tool writes the 32-bit CRC here.
379          * We also use the address of _edata to know the length
380          * of our code.
381          */
382         expected_crc = _edata;
383         length = (unsigned int)&_edata - FLASH_OFFSET_BIOS;
384         actual_crc = crc32((unsigned char *)FLASH_OFFSET_BIOS, length);
385         if(expected_crc == actual_crc)
386                 printf("I: BIOS CRC passed (%08x)\n", actual_crc);
387         else {
388                 printf("W: BIOS CRC failed (expected %08x, got %08x)\n", expected_crc, actual_crc);
389                 printf("W: The system will continue, but expect problems.\n");
390         }
391 }
392
393 static void display_board()
394 {
395         if(brd_desc == NULL) {
396                 printf("E: Running on unknown board (ID=0x%08x), startup aborted.\n", CSR_SYSTEM_ID);
397                 while(1);
398         }
399         printf("I: Running on %s\n", brd_desc->name);
400 }
401
402 #define display_capability(cap, val) if(val) printf("I: "cap": Yes\n"); else printf("I: "cap": No\n")
403
404 static void display_capabilities()
405 {
406         unsigned int cap;
407
408         cap = CSR_CAPABILITIES;
409         display_capability("Mem. card ", cap & CAP_MEMORYCARD);
410         display_capability("AC'97     ", cap & CAP_AC97);
411         display_capability("PFPU      ", cap & CAP_PFPU);
412         display_capability("TMU       ", cap & CAP_TMU);
413         display_capability("Ethernet  ", cap & CAP_ETHERNET);
414         display_capability("FML meter ", cap & CAP_FMLMETER);
415         display_capability("Video in  ", cap & CAP_VIDEOIN);
416         display_capability("MIDI      ", cap & CAP_MIDI);
417         display_capability("DMX       ", cap & CAP_DMX);
418         display_capability("IR        ", cap & CAP_IR);
419         display_capability("USB       ", cap & CAP_USB);
420 }
421
422 static const char banner[] =
423         "\nMILKYMIST(tm) v"VERSION" BIOS\thttp://www.milkymist.org\n"
424         "(c) Copyright 2007, 2008, 2009, 2010 Sebastien Bourdeauducq\n\n"
425         "This program is free software: you can redistribute it and/or modify\n"
426         "it under the terms of the GNU General Public License as published by\n"
427         "the Free Software Foundation, version 3 of the License.\n\n";
428
429 static void boot_sequence()
430 {
431         splash_display();
432         if(test_user_abort()) {
433                 serialboot();
434                 fsboot();
435                 netboot();
436                 printf("E: No boot medium found\n");
437         }
438 }
439
440 int main(int i, char **c)
441 {
442         char buffer[64];
443
444         brd_desc = get_board_desc();
445
446         /* Check for double baud rate */
447         if(brd_desc != NULL) {
448                 if(CSR_GPIO_IN & GPIO_BTN2)
449                         CSR_UART_DIVISOR = brd_desc->clk_frequency/230400/16;
450         }
451
452         /*┬áDisplay a banner as soon as possible to show that the system is alive */
453         putsnonl(banner);
454         
455         crcbios();
456         display_board();
457         display_capabilities();
458         
459         boot_sequence();
460
461         splash_showerr();
462         while(1) {
463                 putsnonl("\e[1mBIOS>\e[0m ");
464                 readstr(buffer, 64);
465                 do_command(buffer);
466         }
467         return 0;
468 }