f39af8407f2d15b025c9306b384642d0730df92f
[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 <hw/hpdmc.h>
28 #include <hw/vga.h>
29 #include <hw/fmlbrg.h>
30 #include <hw/sysctl.h>
31 #include <hw/gpio.h>
32 #include <hw/uart.h>
33
34 #include "boot.h"
35 #include "splash.h"
36
37 const struct board_desc *brd_desc;
38
39 /* SDRAM functions */
40
41 static int dqs_ps;
42
43 static void ddrinit()
44 {
45         volatile unsigned int i;
46
47         putsnonl("I: Initializing SDRAM [DDR200 CL=2 BL=8]...");
48         /* Bring CKE high */
49         CSR_HPDMC_SYSTEM = HPDMC_SYSTEM_BYPASS|HPDMC_SYSTEM_RESET|HPDMC_SYSTEM_CKE;
50         for(i=0;i<2;i++);
51         CSR_HPDMC_BYPASS = 0x400B;      /* Precharge All */
52         for(i=0;i<2;i++);
53         CSR_HPDMC_BYPASS = 0x2000F;     /* Load Extended Mode Register */
54         for(i=0;i<2;i++);
55         CSR_HPDMC_BYPASS = 0x123F;      /* Load Mode Register */
56         for(i=0;i<200;i++);
57         CSR_HPDMC_BYPASS = 0x400B;      /* Precharge All */
58         for(i=0;i<2;i++);
59         CSR_HPDMC_BYPASS = 0xD;         /* Auto Refresh */
60         for(i=0;i<8;i++);
61         CSR_HPDMC_BYPASS = 0xD;         /* Auto Refresh */
62         for(i=0;i<8;i++);
63         CSR_HPDMC_BYPASS = 0x23F;       /* Load Mode Register, Enable DLL */
64         for(i=0;i<200;i++);
65         /* Leave Bypass mode and bring up hardware controller */
66         CSR_HPDMC_SYSTEM = HPDMC_SYSTEM_CKE;
67         
68         /* Set up pre-programmed data bus timings */
69         CSR_HPDMC_IODELAY = HPDMC_IDELAY_RST;
70         for(i=0;i<brd_desc->ddr_idelay;i++)
71                 CSR_HPDMC_IODELAY = HPDMC_IDELAY_CE|HPDMC_IDELAY_INC;
72         
73         dqs_ps = brd_desc->ddr_dqsdelay;
74         for(i=0;i<brd_desc->ddr_dqsdelay;i++) {
75                 CSR_HPDMC_IODELAY = HPDMC_DQSDELAY_CE|HPDMC_DQSDELAY_INC;
76                 while(!(CSR_HPDMC_IODELAY & HPDMC_DQSDELAY_RDY));
77         }
78         
79         printf("OK\n");
80 }
81
82 static int plltest()
83 {
84         int ok1, ok2;
85
86         printf("I: Checking if SDRAM clocking is functional:\n");
87         ok1 = CSR_HPDMC_IODELAY & HPDMC_PLL1_LOCKED;
88         ok2 = CSR_HPDMC_IODELAY & HPDMC_PLL2_LOCKED;
89         printf("I:   PLL#1: %s\n", ok1 ? "Locked" : "Error");
90         printf("I:   PLL#2: %s\n", ok2 ? "Locked" : "Error");
91         return(ok1 && ok2);
92 }
93
94 static int memtest(unsigned int div)
95 {
96         unsigned int *testbuf = (unsigned int *)SDRAM_BASE;
97         unsigned int expected;
98         unsigned int i;
99         unsigned int size;
100
101         putsnonl("I: SDRAM test...");
102
103         size = brd_desc->sdram_size*1024*1024/(4*div);
104         for(i=0;i<size;i++)
105                 testbuf[i] = i;
106
107         /* NB. The Mico32 cache (Level-1) is write-through,
108          * therefore there is no order to flush the cache hierarchy.
109          */
110         asm volatile( /* Invalidate Level-1 data cache */
111                 "wcsr DCC, r0\n"
112                 "nop\n"
113         );
114         flush_bridge_cache(); /* Invalidate Level-2 cache */
115
116         for(i=0;i<size;i++) {
117                 expected = i;
118                 if(testbuf[i] != expected) {
119                         printf("\nE: Failed offset 0x%08x (got 0x%08x, expected 0x%08x)\n", i, testbuf[i], expected);
120                         return 0;
121                 }
122         }
123
124         printf("%u/%uMB tested OK\n", brd_desc->sdram_size/div, brd_desc->sdram_size);
125         return 1;
126 }
127
128 static void calibrate()
129 {
130         int taps;
131         int quit;
132         char c;
133
134         printf("================================\n");
135         printf("DDR SDRAM calibration tool\n");
136         printf("================================\n");
137         printf("Memo:\n");
138         printf("[> Input Delay\n");
139         printf(" r = reset to 0 taps\n");
140         printf(" u = add 1 tap\n");
141         printf(" d = remove 1 tap\n");
142         printf("[> DQS output phase\n");
143         printf(" U = increase phase\n");
144         printf(" D = decrease phase\n");
145         printf("[> Misc\n");
146         printf(" t = load image to framebuffer\n");
147         printf(" q = quit\n");
148         
149         CSR_VGA_RESET = 0;
150         
151         CSR_HPDMC_IODELAY = HPDMC_IDELAY_RST;
152         
153         taps = 0;
154         quit = 0;
155         while(!quit) {
156                 printf("Taps: %02d (78ps each) - DQS phase: %04d/255\r", taps, dqs_ps);
157                 c = readchar();
158                 switch(c) {
159                         case 'q':
160                                 quit = 1;
161                                 break;
162                         case 'r':
163                                 taps = 0;
164                                 CSR_HPDMC_IODELAY = HPDMC_IDELAY_RST;
165                                 break;
166                         case 'u':
167                                 if(taps < 63) {
168                                         taps++;
169                                         CSR_HPDMC_IODELAY = HPDMC_IDELAY_CE|HPDMC_IDELAY_INC;
170                                 }
171                                 break;
172                         case 'd':
173                                 if(taps > 0) {
174                                         taps--;
175                                         CSR_HPDMC_IODELAY = HPDMC_IDELAY_CE;
176                                 }
177                                 break;
178                         case 'U':
179                                 if(dqs_ps < 255) {
180                                         dqs_ps++;
181                                         CSR_HPDMC_IODELAY = HPDMC_DQSDELAY_CE|HPDMC_DQSDELAY_INC;
182                                         while(!(CSR_HPDMC_IODELAY & HPDMC_DQSDELAY_RDY));
183                                 }
184                                 break;
185                         case 'D':
186                                 if(dqs_ps > 0) {
187                                         dqs_ps--;
188                                         CSR_HPDMC_IODELAY = HPDMC_DQSDELAY_CE;
189                                         while(!(CSR_HPDMC_IODELAY & HPDMC_DQSDELAY_RDY));
190                                 }
191                                 break;
192                         case 't':
193                                 splash_display((void *)SDRAM_BASE);
194                                 break;
195                 }
196         }
197
198         CSR_VGA_RESET = VGA_RESET;
199
200         printf("\n");
201 }
202
203 /* General address space functions */
204
205 #define NUMBER_OF_BYTES_ON_A_LINE 16
206 static void dump_bytes(unsigned int *ptr, int count, unsigned addr)
207 {
208         char *data = (char *)ptr;
209         int line_bytes = 0, i = 0;
210
211         putsnonl("Memory dump:");
212         while(count > 0){
213                 line_bytes =
214                         (count > NUMBER_OF_BYTES_ON_A_LINE)?
215                                 NUMBER_OF_BYTES_ON_A_LINE : count;
216
217                 printf("\n0x%08x  ", addr);
218                 for(i=0;i<line_bytes;i++)
219                         printf("%02x ", *(unsigned char *)(data+i));
220         
221                 for(;i<NUMBER_OF_BYTES_ON_A_LINE;i++)
222                         printf("   ");
223         
224                 printf(" ");
225
226                 for(i=0;i<line_bytes;i++) {
227                         if((*(data+i) < 0x20) || (*(data+i) > 0x7e))
228                                 printf(".");
229                         else
230                                 printf("%c", *(data+i));
231                 }       
232
233                 for(;i<NUMBER_OF_BYTES_ON_A_LINE;i++)
234                         printf(" ");
235
236                 data += (char)line_bytes;
237                 count -= line_bytes;
238                 addr += line_bytes;
239         }
240         printf("\n");
241 }
242
243
244 static void mr(char *startaddr, char *len)
245 {
246         char *c;
247         unsigned int *addr;
248         unsigned int length;
249
250         if(*startaddr == 0) {
251                 printf("mr <address> [length]\n");
252                 return;
253         }
254         addr = (unsigned *)strtoul(startaddr, &c, 0);
255         if(*c != 0) {
256                 printf("incorrect address\n");
257                 return;
258         }
259         if(*len == 0) {
260                 length = 1;
261         } else {
262                 length = strtoul(len, &c, 0);
263                 if(*c != 0) {
264                         printf("incorrect length\n");
265                         return;
266                 }
267         }
268
269         dump_bytes(addr, length, (unsigned)addr);
270 }
271
272 static void mw(char *addr, char *value, char *count)
273 {
274         char *c;
275         unsigned int *addr2;
276         unsigned int value2;
277         unsigned int count2;
278         unsigned int i;
279
280         if((*addr == 0) || (*value == 0)) {
281                 printf("mw <address> <value> [count]\n");
282                 return;
283         }
284         addr2 = (unsigned int *)strtoul(addr, &c, 0);
285         if(*c != 0) {
286                 printf("incorrect address\n");
287                 return;
288         }
289         value2 = strtoul(value, &c, 0);
290         if(*c != 0) {
291                 printf("incorrect value\n");
292                 return;
293         }
294         if(*count == 0) {
295                 count2 = 1;
296         } else {
297                 count2 = strtoul(count, &c, 0);
298                 if(*c != 0) {
299                         printf("incorrect count\n");
300                         return;
301                 }
302         }
303         for (i=0;i<count2;i++) *addr2++ = value2;
304 }
305
306 static void mc(char *dstaddr, char *srcaddr, char *count)
307 {
308         char *c;
309         unsigned int *dstaddr2;
310         unsigned int *srcaddr2;
311         unsigned int count2;
312         unsigned int i;
313
314         if((*dstaddr == 0) || (*srcaddr == 0)) {
315                 printf("mc <dst> <src> [count]\n");
316                 return;
317         }
318         dstaddr2 = (unsigned int *)strtoul(dstaddr, &c, 0);
319         if(*c != 0) {
320                 printf("incorrect destination address\n");
321                 return;
322         }
323         srcaddr2 = (unsigned int *)strtoul(srcaddr, &c, 0);
324         if(*c != 0) {
325                 printf("incorrect source address\n");
326                 return;
327         }
328         if(*count == 0) {
329                 count2 = 1;
330         } else {
331                 count2 = strtoul(count, &c, 0);
332                 if(*c != 0) {
333                         printf("incorrect count\n");
334                         return;
335                 }
336         }
337         for (i=0;i<count2;i++) *dstaddr2++ = *srcaddr2++;
338 }
339
340 static void crc(char *startaddr, char *len)
341 {
342         char *c;
343         char *addr;
344         unsigned int length;
345
346         if((*startaddr == 0)||(*len == 0)) {
347                 printf("crc <address> <length>\n");
348                 return;
349         }
350         addr = (char *)strtoul(startaddr, &c, 0);
351         if(*c != 0) {
352                 printf("incorrect address\n");
353                 return;
354         }
355         length = strtoul(len, &c, 0);
356         if(*c != 0) {
357                 printf("incorrect length\n");
358                 return;
359         }
360
361         printf("CRC32: %08x\n", crc32((unsigned char *)addr, length));
362 }
363
364 /* CF filesystem functions */
365
366 static int lscb(const char *filename, const char *longname, void *param)
367 {
368         printf("%12s [%s]\n", filename, longname);
369         return 1;
370 }
371
372 static void ls()
373 {
374         if(brd_desc->memory_card == MEMCARD_NONE) {
375                 printf("E: No memory card on this board\n");
376                 return;
377         }
378         cffat_init();
379         cffat_list_files(lscb, NULL);
380         cffat_done();
381 }
382
383 static void load(char *filename, char *addr)
384 {
385         char *c;
386         unsigned int *addr2;
387
388         if(brd_desc->memory_card == MEMCARD_NONE) {
389                 printf("E: No memory card on this board\n");
390                 return;
391         }
392
393         if((*filename == 0) || (*addr == 0)) {
394                 printf("load <filename> <address>\n");
395                 return;
396         }
397         addr2 = (unsigned *)strtoul(addr, &c, 0);
398         if(*c != 0) {
399                 printf("incorrect address\n");
400                 return;
401         }
402         cffat_init();
403         cffat_load(filename, (char *)addr2, 16*1024*1024, NULL);
404         cffat_done();
405 }
406
407 /* Init + command line */
408
409 static void help()
410 {
411         puts("This is the Milkymist BIOS debug shell.");
412         puts("It is used for system development and maintainance, and not");
413         puts("for regular operation.\n");
414         puts("Available commands:");
415         puts("plltest    - print status of the SDRAM clocking PLLs");
416         puts("memtest    - extended SDRAM test");
417         puts("calibrate  - run DDR SDRAM calibration tool");
418         puts("flush      - flush FML bridge cache");
419         puts("mr         - read address space");
420         puts("mw         - write address space");
421         puts("mc         - copy address space");
422         puts("crc        - compute CRC32 of a part of the address space");
423         puts("ls         - list files on the memory card");
424         puts("load       - load a file from the memory card");
425         puts("serialboot - attempt SFL boot");
426         puts("cardboot   - attempt booting from memory card");
427         puts("reboot     - system reset");
428 }
429
430 static char *get_token(char **str)
431 {
432         char *c, *d;
433
434         c = (char *)strchr(*str, ' ');
435         if(c == NULL) {
436                 d = *str;
437                 *str = *str+strlen(*str);
438                 return d;
439         }
440         *c = 0;
441         d = *str;
442         *str = c+1;
443         return d;
444 }
445
446 static void do_command(char *c)
447 {
448         char *token;
449
450         token = get_token(&c);
451
452         if(strcmp(token, "plltest") == 0) plltest();
453         else if(strcmp(token, "memtest") == 0) memtest(1);
454         else if(strcmp(token, "calibrate") == 0) calibrate();
455         else if(strcmp(token, "flush") == 0) flush_bridge_cache();
456
457         else if(strcmp(token, "mr") == 0) mr(get_token(&c), get_token(&c));
458         else if(strcmp(token, "mw") == 0) mw(get_token(&c), get_token(&c), get_token(&c));
459         else if(strcmp(token, "mc") == 0) mc(get_token(&c), get_token(&c), get_token(&c));
460         else if(strcmp(token, "crc") == 0) crc(get_token(&c), get_token(&c));
461         
462         else if(strcmp(token, "ls") == 0) ls();
463         else if(strcmp(token, "load") == 0) load(get_token(&c), get_token(&c));
464         
465         else if(strcmp(token, "serialboot") == 0) serialboot();
466         else if(strcmp(token, "cardboot") == 0) cardboot(0);
467
468         else if(strcmp(token, "reboot") == 0) reboot();
469         
470         else if(strcmp(token, "help") == 0) help();
471         
472         else if(strcmp(token, "") != 0)
473                 printf("Command not found\n");
474 }
475
476 static int test_user_abort()
477 {
478         unsigned int i;
479         char c;
480         
481         puts("I: Press Q to abort boot");
482         for(i=0;i<4000000;i++) {
483                 if(readchar_nonblock()) {
484                         c = readchar();
485                         if(c == 'Q') {
486                                 puts("I: Aborted boot on user request");
487                                 return 0;
488                         }
489                 }
490         }
491         return 1;
492 }
493
494 extern unsigned int _edata;
495
496 static void crcbios()
497 {
498         unsigned int length;
499         unsigned int expected_crc;
500         unsigned int actual_crc;
501         
502         /*
503          * _edata is located right after the end of the flat
504          * binary image. The CRC tool writes the 32-bit CRC here.
505          * We also use the address of _edata to know the length
506          * of our code.
507          */
508         expected_crc = _edata;
509         length = (unsigned int)&_edata;
510         actual_crc = crc32((unsigned char *)0, length);
511         if(expected_crc == actual_crc)
512                 printf("I: BIOS CRC passed (%08x)\n", actual_crc);
513         else {
514                 printf("W: BIOS CRC failed (expected %08x, got %08x)\n", expected_crc, actual_crc);
515                 printf("W: The system will continue, but expect problems.\n");
516         }
517 }
518
519 static void display_board()
520 {
521         if(brd_desc == NULL) {
522                 printf("E: Running on unknown board (ID=0x%08x), startup aborted.\n", CSR_SYSTEM_ID);
523                 while(1);
524         }
525         printf("I: Running on %s\n", brd_desc->name);
526 }
527
528 static const char banner[] =
529         "\nMILKYMIST(tm) v"VERSION" BIOS\thttp://www.milkymist.org\n"
530         "(c) 2007, 2008, 2009, 2010 Sebastien Bourdeauducq\n\n"
531         "This program is free software: you can redistribute it and/or modify\n"
532         "it under the terms of the GNU General Public License as published by\n"
533         "the Free Software Foundation, version 3 of the License.\n\n";
534
535 static void boot_sequence()
536 {
537         splash_display((void *)(SDRAM_BASE+1024*1024*(brd_desc->sdram_size-4)));
538         if(test_user_abort()) {
539                 serialboot(1);
540                 if(brd_desc->memory_card != MEMCARD_NONE) {
541                         if(CSR_GPIO_IN & GPIO_DIP1)
542                                 cardboot(1);
543                         else
544                                 cardboot(0);
545                 }
546                 printf("E: No boot medium found\n");
547         }
548 }
549
550 int main(int i, char **c)
551 {
552         char buffer[64];
553
554         brd_desc = get_board_desc();
555
556         /* Check for double baud rate */
557         if(brd_desc != NULL) {
558                 if(CSR_GPIO_IN & GPIO_DIP2)
559                         CSR_UART_DIVISOR = brd_desc->clk_frequency/230400/16;
560         }
561
562         /*┬áDisplay a banner as soon as possible to show that the system is alive */
563         putsnonl(banner);
564         
565         crcbios();
566         display_board();
567         
568         if(plltest()) {
569                 ddrinit();
570                 flush_bridge_cache();
571
572                 if(memtest(8))
573                         boot_sequence();
574                 else
575                         printf("E: Aborted boot on memory error\n");
576         } else
577                 printf("E: Faulty SDRAM clocking\n");
578
579         splash_showerr();
580         while(1) {
581                 putsnonl("\e[1mBIOS>\e[0m ");
582                 readstr(buffer, 64);
583                 do_command(buffer);
584         }
585         return 0;
586 }