main.c revision 1.2.4.1 1 /*-
2 * Copyright (c) 2012 The NetBSD Foundation, Inc.
3 * All rights reserved.
4 *
5 * This code is derived from software contributed to The NetBSD Foundation
6 * by Paul Fleischer <paul (at) xpg.dk>
7 *
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
10 * are met:
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, this list of conditions and the following disclaimer.
13 * 2. Redistributions in binary form must reproduce the above copyright
14 * notice, this list of conditions and the following disclaimer in the
15 * documentation and/or other materials provided with the distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 * POSSIBILITY OF SUCH DAMAGE.
28 */
29 #include <sys/types.h>
30
31 #include <arm/armreg.h>
32 #include <arm/s3c2xx0/s3c2440reg.h>
33
34 #include <netinet/in.h>
35 #include <netinet/in_systm.h>
36
37 #include <lib/libkern/libkern.h>
38 #include <lib/libsa/stand.h>
39 #include <lib/libsa/loadfile.h>
40 #include <lib/libsa/iodesc.h>
41
42 #include <arch/evbarm/mini2440/mini2440_bootinfo.h>
43
44 #define CSR_READ(reg) \
45 *(volatile uint32_t *)(reg)
46 #define CSR_WRITE(reg, val) do { \
47 *(volatile uint32_t *)((reg)) = val; \
48 } while (0)
49
50 #define UART_BAUDRATE 115200
51 #define S3C2XX0_XTAL_CLK 12000000
52 #define BOOTINFO_ADDR 0x31500000
53
54 /* Macros to turn on/off LEDs. Numbering is 1-4. */
55 #define LED_REG (volatile uint16_t*)(S3C2440_GPIO_BASE+GPIO_PBDAT)
56 #define CLEAR_LEDS() *LED_REG = *LED_REG | 0x1e0
57 #define LED_ON(led) *LED_REG = *LED_REG & ( ~(1<<(led+4)) & 0x1E0 )
58 #define LED_OFF(led) *LED_REG = *LED_REG | ( ~(1<<(led+4)) & 0x1E0 )
59
60 /* Local variables */
61 static time_t wallclock = 0;
62 static uint32_t timer_inc_rate;
63 void *bootinfo;
64 int bi_size;
65 char *bi_next;
66
67 #define STR_EXPAND(tok) #tok
68 #define STR(tok) STR_EXPAND(tok)
69
70 #if defined(DEFAULT_BOOTFILE)
71 static char *default_boot=STR(DEFAULT_BOOTFILE);
72 #else
73 static char *default_boot="net:";
74 #endif
75
76 time_t getsecs();
77 time_t getusecs();
78
79 /* Local functions */
80 static void s3c24x0_clock_freq2(vaddr_t clkman_base, int *fclk, int *hclk,
81 int *pclk);
82 static void uart_init(uint32_t pclk);
83 static void time_init(uint32_t pclk);
84 static void bi_init(void *addr);
85 static void bi_add(void *new, int type, int size);
86 static void parse_mac_address(const char *str, uint8_t *enaddr);
87 static void brdsetup(void);
88 static void iomux(int, const char *);
89
90 extern void* dm9k_init(unsigned int tag, void *macaddr);
91
92 /* External variables */
93 extern char bootprog_name[], bootprog_rev[];
94
95 /* External functions */
96 extern void netif_match(unsigned int tag, uint8_t *macaddr);
97 /* extern int sdif_init(unsigned int tag);*/
98
99 /* Global variables */
100 uint32_t socmodel;
101 int pclk;
102 struct btinfo_rootdevice bi_rdev;
103
104 /* This is not very flexible, as only one net device is allowed */
105 struct btinfo_net bi_net;
106
107 struct btinfo_bootpath bi_path;
108
109 void
110 main(int argc, char *argv[])
111 {
112 int fclk, hclk;
113 int fd;
114 unsigned long marks[MARK_MAX];
115 unsigned char hdr[0x26];
116 void (*entry)(void*);
117 unsigned elfpriv;
118 char *bootfile;
119 char *bf;
120 bool kernel_loaded;
121 uint8_t enaddr[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
122
123 socmodel = CSR_READ(S3C2440_GPIO_BASE + GPIO_GSTATUS1);
124
125 brdsetup();
126
127 /* Give some indication that main() has been reached */
128 CLEAR_LEDS();
129 LED_ON(4);
130
131 /* Next, we setup the clock of the S3C2440 such that we are not
132 dependent on any other bootloader in this regard.
133 Target FCLK is 405MHz, and we assume an input crystal of 12MHz
134 */
135 *(volatile uint32_t*)(S3C2440_CLKMAN_BASE+CLKMAN_MPLLCON) =
136 ((0x7F << PLLCON_MDIV_SHIFT) & PLLCON_MDIV_MASK) |
137 ((2 << PLLCON_PDIV_SHIFT) & PLLCON_PDIV_MASK) |
138 ((1 << PLLCON_SDIV_SHIFT) & PLLCON_SDIV_MASK);
139 *(volatile uint32_t*)(S3C2440_CLKMAN_BASE+CLKMAN_UPLLCON) =
140 ((0x38 << PLLCON_MDIV_SHIFT) & PLLCON_MDIV_MASK) |
141 ((2 << PLLCON_PDIV_SHIFT) & PLLCON_PDIV_MASK) |
142 ((2 << PLLCON_SDIV_SHIFT) & PLLCON_SDIV_MASK);
143
144 LED_ON(1);
145
146 s3c24x0_clock_freq2(S3C2440_CLKMAN_BASE, &fclk, &hclk, &pclk);
147
148 uart_init(pclk);
149 time_init(pclk);
150
151 /* Let the user know we are alive */
152 printf("\n");
153 printf(">> %s boot2440, revision %s\n", bootprog_name, bootprog_rev);
154 printf("SoC model:");
155 switch (socmodel) {
156 case 0x32440000:
157 printf(" S3C2440"); break;
158 case 0x32440001:
159 printf(" S3C2440A"); break;
160 }
161 printf(" (chipid %08x)\n", socmodel);
162
163 bootinfo = (void*) BOOTINFO_ADDR;
164 bi_init(bootinfo);
165
166 bi_net.devname[0] = 0;
167 bi_path.bootpath[0] = 0;
168
169 /* Try to get boot arguments from any previous boot-loader */
170 {
171 struct btinfo_bootstring ba;
172 int j, i;
173
174 j = 0;
175 for (i = 0; i < argc; i++) {
176 if (j == MAX_BOOT_STRING-1) {
177 ba.bootstring[j] = '\0';
178 continue;
179 }
180 if (strncmp(argv[i], "mac=", 4) == 0) {
181 parse_mac_address(argv[i]+4, enaddr);
182 } else {
183 if (j != 0)
184 ba.bootstring[j++] = ' ';
185
186 strncpy(ba.bootstring+j, argv[i], MAX_BOOT_STRING-j);
187 j += strlen(argv[i]);
188 }
189 }
190 bi_add(&ba, BTINFO_BOOTSTRING, sizeof(ba));
191 }
192
193 LED_ON(3);
194
195 if (argc > 1) {
196 bf = argv[argc-1];
197 } else {
198 bf = default_boot;
199 }
200
201 /* Detect networking devices */
202 netif_match(0, enaddr);
203
204 kernel_loaded = FALSE;
205 do {
206 bootfile = strsep(&bf, ";");
207 printf("Trying \"%s\"...\n", bootfile);
208 fd = open(bootfile, 0);
209 if (fd < 0) {
210 printf("Failed: %d\n", errno);
211 close(fd);
212 continue;
213 }
214
215 if (fdloadfile(fd, marks, LOAD_ALL) == 0) {
216 kernel_loaded = TRUE;
217 break;
218 }
219 } while(bf != NULL);
220
221 if (!kernel_loaded) {
222 panic("Failed to load kernel\n");
223 _rtt();
224 }
225
226 #if 1
227 /* Set MAC address of the 'dme' net device, if
228 * it isn't set already */
229 if (bi_net.devname[0] == 0) {
230 uint8_t en[6] = {DM9000MAC};
231 snprintf(bi_net.devname, sizeof(bi_net.devname), "dme");
232 bi_net.cookie = 0;
233
234 memcpy(bi_net.mac_address, en, sizeof(bi_net.mac_address));
235 }
236 #endif
237 /*
238 * ARM ELF header has a distinctive value in "private flags"
239 * field of offset [0x24:25];
240 * - NetBSD 02 06
241 * - Linux 02 00 (2.4) or 02 02 (2.6)
242 */
243 lseek(fd, (off_t)0, SEEK_SET);
244 read(fd, &hdr, sizeof(hdr));
245 memcpy(&elfpriv, &hdr[0x24], sizeof(elfpriv));
246
247 entry = (void *)marks[MARK_ENTRY];
248 if (elfpriv == 0x0602) {
249 struct btinfo_symtab bi_syms;
250
251 bi_syms.nsym = marks[MARK_NSYM];
252 bi_syms.ssym = (void*)marks[MARK_SYM];
253 bi_syms.esym = (void*)marks[MARK_END];
254 bi_add(&bi_syms, BTINFO_SYMTAB, sizeof(bi_syms));
255 if (bi_path.bootpath[0] != 0)
256 bi_add(&bi_path, BTINFO_BOOTPATH, sizeof(bi_path));
257 bi_add(&bi_rdev, BTINFO_ROOTDEVICE, sizeof(bi_rdev));
258 if (bi_net.devname[0] != 0 )
259 bi_add(&bi_net, BTINFO_NET, sizeof(bi_net));
260 } else {
261 printf("Loaded object is not NetBSD ARM ELF");
262 _rtt();
263 }
264
265 printf("entry=%p, nsym=%lu, ssym=%p, esym=%p\n",
266 (void *)marks[MARK_ENTRY],
267 marks[MARK_NSYM],
268 (void *)marks[MARK_SYM],
269 (void *)marks[MARK_END]);
270 (*entry)(bootinfo);
271
272 printf("exec returned, restarting...\n");
273 _rtt();
274 }
275
276 void
277 uart_init(uint32_t pclk)
278 {
279 /* Setup UART0 clocking: Use PCLK */
280 *(volatile uint32_t*)(S3C2440_UART_BASE(0)+SSCOM_UBRDIV) =
281 (pclk/(UART_BAUDRATE*16)) - 1;
282
283 *(volatile uint32_t*)(S3C2440_UART_BASE(0)+SSCOM_UCON) =
284 UCON_TXMODE_INT | UCON_RXMODE_INT;
285
286 *(volatile uint32_t*)(S3C2440_UART_BASE(0)+SSCOM_ULCON) =
287 ULCON_PARITY_NONE | ULCON_LENGTH_8;
288
289 *(volatile uint32_t*)(S3C2440_UART_BASE(0)+SSCOM_UFCON) =
290 UFCON_TXTRIGGER_0 | UFCON_TXFIFO_RESET | UFCON_FIFO_ENABLE;
291 }
292
293 static uint32_t countdown_duration;
294
295 static
296 void time_init(uint32_t pclk)
297 {
298 /* Configure timer0 to be as slow as possible:
299 Prescaler = 255
300 Divider = 16
301 */
302
303 /* First, configure the prescaler */
304 *(volatile uint32_t*)(S3C2440_TIMER_BASE+TIMER_TCFG0) = 0xff;
305
306 /* Next, the divider */
307 *(volatile uint32_t*)(S3C2440_TIMER_BASE+TIMER_TCFG1) |=
308 (TCFG1_MUX_DIV16 <<TCFG1_MUX_SHIFT(0)) & TCFG1_MUX_MASK(0);
309
310 *(volatile uint32_t*)(S3C2440_TIMER_BASE+TIMER_TCON) =
311 TCON_MANUALUPDATE(0);
312 *(volatile uint32_t*)(S3C2440_TIMER_BASE+TIMER_TCNTB(0)) =
313 0xffff;
314 *(volatile uint32_t*)(S3C2440_TIMER_BASE+TIMER_TCON) =
315 TCON_START(0);
316
317
318 /* Timer count down duration */
319 countdown_duration = 65535/(pclk/256/16);
320 timer_inc_rate = pclk/256/16;
321 // printf("Countdown duration is: %ds\n", countdown_duration);
322 #if 0
323 {
324 /* Timer test */
325 time_t time, old_time;
326
327 while(1) {
328 time = old_time = getsecs();
329 do {
330 time = getsecs();
331 } while(time == old_time);
332 printf("Count %u\n", (int)time);
333 }
334 }
335 #endif
336 }
337
338 time_t
339 getsecs()
340 {
341 time_t secs = getusecs()/1000000;
342 return secs;
343 }
344
345 time_t
346 getusecs() {
347 uint32_t count;
348 //do {
349 count = *(volatile uint32_t*)(S3C2440_TIMER_BASE+TIMER_TCNTO(0));
350 //} while( count > 65500);
351
352 *(volatile uint32_t*)(S3C2440_TIMER_BASE+TIMER_TCON) =
353 TCON_MANUALUPDATE(0);
354 *(volatile uint32_t*)(S3C2440_TIMER_BASE+TIMER_TCNTB(0)) =
355 0xffff;
356 *(volatile uint32_t*)(S3C2440_TIMER_BASE+TIMER_TCON) =
357 TCON_START(0);
358
359 wallclock += ((65535-count)*1000000) / timer_inc_rate;
360
361 return wallclock;
362 }
363
364 void
365 usleep(int us) {
366 uint32_t count;
367 uint32_t target_clock = wallclock+us;
368
369 while( wallclock < target_clock) {
370 do {
371 count = *(volatile uint32_t*)(S3C2440_TIMER_BASE+TIMER_TCNTO(0));
372 } while( count > 65500);
373
374 *(volatile uint32_t*)(S3C2440_TIMER_BASE+TIMER_TCON) =
375 TCON_MANUALUPDATE(0);
376 *(volatile uint32_t*)(S3C2440_TIMER_BASE+TIMER_TCNTB(0)) =
377 0xffff;
378 *(volatile uint32_t*)(S3C2440_TIMER_BASE+TIMER_TCON) =
379 TCON_START(0);
380
381 wallclock += ((65535-count)*1000000) / timer_inc_rate;
382 }
383 }
384
385
386 void
387 mini2440_panic()
388 {
389 int i, l;
390 int v;
391 while(1) {
392 CLEAR_LEDS();
393 for(l=0; l<0xffffff; l++) {
394 v = *((int*)(S3C2440_TIMER_BASE+TIMER_TCNTO(0)));
395 }
396 for(i=1; i<=4; i++) {
397 LED_ON(i);
398 }
399 for(l=0; l<0xffffff; l++) {
400 v = *((int*)(S3C2440_TIMER_BASE+TIMER_TCNTO(0)));
401 }
402 __USE(v);
403 }
404 }
405
406 void
407 s3c24x0_clock_freq2(vaddr_t clkman_base, int *fclk, int *hclk, int *pclk)
408 {
409 uint32_t pllcon, divn, camdivn;
410 int mdiv, pdiv, sdiv;
411 uint32_t f, h, p;
412
413 pllcon = *(volatile uint32_t *)(clkman_base + CLKMAN_MPLLCON);
414 divn = *(volatile uint32_t *)(clkman_base + CLKMAN_CLKDIVN);
415 camdivn = *(volatile uint32_t *)(clkman_base + CLKMAN_CAMDIVN);
416
417 mdiv = (pllcon & PLLCON_MDIV_MASK) >> PLLCON_MDIV_SHIFT;
418 pdiv = (pllcon & PLLCON_PDIV_MASK) >> PLLCON_PDIV_SHIFT;
419 sdiv = (pllcon & PLLCON_SDIV_MASK) >> PLLCON_SDIV_SHIFT;
420
421 f = ((mdiv + 8) * S3C2XX0_XTAL_CLK) / ((pdiv + 2) * (1 << sdiv)) * 2;
422 h = f;
423
424 /* HDIVN of CLKDIVN can have 4 distinct values */
425 switch( (divn & CLKDIVN_HDIVN_MASK) >> CLKDIVN_HDIVN_SHIFT )
426 {
427 case 0:
428 /* 00b: HCLK = FCLK/1*/
429 break;
430 case 1:
431 /* 01b: HCLK = FCLK/2*/
432 h /= 2;
433 break;
434 case 2:
435 /* 10b: HCLK = FCLK/4 when CAMDIVN[9] (HCLK4_HALF) = 0
436 * HCLK = FCLK/8 when CAMDIVN[9] (HCLK4_HALF) = 1 */
437 if( camdivn & CLKCAMDIVN_HCLK4_HALF )
438 h /= 8;
439 else
440 h /= 4;
441 break;
442 case 3:
443 /* 11b: HCLK = FCLK/3 when CAMDIVN[8] (HCLK3_HALF) = 0
444 * HCLK = FCLK/6 when CAMDIVN[8] (HCLK3_HALF) = 1 */
445 if( camdivn & CLKCAMDIVN_HCLK3_HALF )
446 h /= 6;
447 else
448 h /= 3;
449 break;
450 }
451
452 p = h;
453
454 if (divn & CLKDIVN_PDIVN)
455 p /= 2;
456
457 if (fclk) *fclk = f;
458 if (hclk) *hclk = h;
459 if (pclk) *pclk = p;
460 }
461
462 void
463 putchar(int c)
464 {
465 uint32_t stat;
466
467 if (c == '\n')
468 putchar('\r');
469
470 do {
471 stat = CSR_READ(S3C2440_UART_BASE(0) + SSCOM_UTRSTAT);
472 } while ((stat & UTRSTAT_TXEMPTY) == 0);
473
474 CSR_WRITE(S3C2440_UART_BASE(0) + SSCOM_UTXH, c);
475 }
476
477 void
478 _rtt()
479 {
480 int cpsr_save, tmp;
481 /* Disable interrupts */
482 __asm volatile("mrs %0, cpsr;"
483 "orr %1, %0, %2;"
484 "msr cpsr_c, %1;"
485 : "=r" (cpsr_save), "=r" (tmp)
486 : "I" (I32_bit)
487 );
488
489 /* Disable MMU */
490 __asm volatile("mrc p15, 0, %0, c1, c0, 0;"
491 "bic %0, %0, %1;"
492 "mcr p15, 0, %0, c1, c0, 0;"
493 : "=r" (tmp)
494 : "I" (CPU_CONTROL_MMU_ENABLE)
495 );
496
497 /* Configure watchdog to fire now */
498 *(volatile uint32_t *)(S3C2440_WDT_BASE + WDT_WTCON) =
499 (0 << WTCON_PRESCALE_SHIFT) | WTCON_ENABLE |
500 WTCON_CLKSEL_16 | WTCON_ENRST;
501 __builtin_unreachable();
502 }
503
504 void
505 bi_init(void *addr)
506 {
507 struct btinfo_magic bi_magic;
508
509 memset(addr, 0, BOOTINFO_MAXSIZE);
510 bi_next = (char*) addr;
511 bi_size = 0;
512
513 bi_magic.magic = BOOTINFO_MAGIC;
514 bi_add(&bi_magic, BTINFO_MAGIC, sizeof(bi_magic));
515 }
516
517
518 void
519 bi_add(void *new, int type, int size)
520 {
521 struct btinfo_common *bi;
522
523 if (bi_size + size > BOOTINFO_MAXSIZE)
524 return;
525
526 bi = new;
527 bi->next = size;
528 bi->type = type;
529 memcpy(bi_next, new, size);
530 bi_next += size;
531 }
532
533 static void
534 parse_mac_address(const char *str, uint8_t *enaddr)
535 {
536 int i;
537 char *next = (char*)str;
538
539 for(i=0;i<6;i++) {
540 str = next;
541 enaddr[i] = (unsigned char)strtoll(str, &next, 16);
542 if( *next == ':' ) {
543 next++;
544 } else {
545 break;
546 }
547 }
548 }
549
550 static void
551 brdsetup(void)
552 {
553 /*
554 * MINI2440 pin usage summary
555 *
556 * B5 output LED1 control
557 * B6 output LED2 control
558 * B7 output LED3 control
559 * B8 output LED4 control
560 * G0 EINT8 K1 button
561 * G3 EINT11 K2 button
562 * G5 EINT13 K3 button
563 * G6 EINT14 K4 button
564 * G7 EINT15 K5 button
565 * G11 EINT19 K6 button
566 * F7 EINT7 DM9000 interrupt
567 * G12 EINT20 camera interrupt
568 * G8 input SD card presense detect
569 * H8 input SD write protect sense
570 * B0 TOUT0 buzzer PWM
571 * B1 TOUT1 LCD backlight PWM
572 * B2 output UDA1341 audio L3MODE
573 * B3 output UDA1341 audio L3DATA
574 * B4 output UDA1341 audio L3LOCK
575 *
576 * A21, A11, G15, G14, G13: not used.
577 *
578 * i input sense
579 * o output control
580 * 2 function 2
581 * 3 function 3
582 * 0 output control (A only)
583 * 1 function 1 (A only)
584 * ./x no function, not connected or don't-care
585 *
586 * A ........ .1x11111 1111x111 11111111
587 * B .....22o ooooooo2
588 * C 22222222 22222222
589 * D 22222222 22222222
590 * E 22222222 22222222
591 * F ........ 22222222
592 * G xxx2222i 22232322
593 * H .....22i 22222222
594 * J ...22222 22222222
595 */
596 iomux('A', "........ .1x11111 1111x111 11111111");
597 iomux('B', ".....22o ooooooo2");
598 iomux('C', "22222222 22222222");
599 iomux('D', "22222222 22222222");
600 iomux('E', "22222222 22222222");
601 iomux('F', "........ 22222222");
602 iomux('G', "xxx2222i 22232322");
603 iomux('H', ".....22i 22222222");
604 iomux('J', "...22222 22222222");
605
606 /* mask all possible external interrupt source [23:3] */
607 CSR_WRITE(S3C2440_GPIO_BASE + GPIO_EINTMASK, ~0);
608 }
609
610 static void
611 iomux(int grp, const char *cnf)
612 {
613 uint32_t con;
614 int sft, i, v;
615
616 con = v = 0;
617 sft = (grp != 'A') ? 2 : 1;
618 for (i = 0; cnf[i] != '\0'; i++) {
619 switch (cnf[i]) {
620 case 'i':
621 case '0':
622 case '.':
623 case 'x':
624 v = 0; break;
625 case 'o':
626 case '1':
627 v = 1; break;
628 case '2':
629 v = 2; break;
630 case '3':
631 v = 3; break;
632 default:
633 continue;
634 }
635 con = (con << sft) | v;
636 }
637 CSR_WRITE(S3C2440_GPIO_BASE + 0x10 * (grp - 'A'), con);
638 }
639