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