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@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
main(int argc,char * argv[])110 main(int argc, char *argv[])
111 {
112 int fclk, hclk;
113 int fd;
114 unsigned long marks[MARK_MAX];
115 unsigned char hdr[0x28];
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-x027];
240 * - NetBSD 02 06 (oarm)
241 * - Linux 02 00 (2.4) or 02 02 (2.6)
242 * - NetBSD 02 00 00 05 (earm)
243 */
244 lseek(fd, (off_t)0, SEEK_SET);
245 read(fd, &hdr, sizeof(hdr));
246 memcpy(&elfpriv, &hdr[0x24], sizeof(elfpriv));
247
248 entry = (void *)marks[MARK_ENTRY];
249 if (elfpriv == 0x0602 || elfpriv == 0x5000002) {
250 struct btinfo_symtab bi_syms;
251
252 bi_syms.nsym = marks[MARK_NSYM];
253 bi_syms.ssym = (void*)marks[MARK_SYM];
254 bi_syms.esym = (void*)marks[MARK_END];
255 bi_add(&bi_syms, BTINFO_SYMTAB, sizeof(bi_syms));
256 if (bi_path.bootpath[0] != 0)
257 bi_add(&bi_path, BTINFO_BOOTPATH, sizeof(bi_path));
258 bi_add(&bi_rdev, BTINFO_ROOTDEVICE, sizeof(bi_rdev));
259 if (bi_net.devname[0] != 0 )
260 bi_add(&bi_net, BTINFO_NET, sizeof(bi_net));
261 } else {
262 printf("Loaded object is not NetBSD ARM ELF");
263 _rtt();
264 }
265
266 printf("entry=%p, nsym=%lu, ssym=%p, esym=%p\n",
267 (void *)marks[MARK_ENTRY],
268 marks[MARK_NSYM],
269 (void *)marks[MARK_SYM],
270 (void *)marks[MARK_END]);
271 (*entry)(bootinfo);
272
273 printf("exec returned, restarting...\n");
274 _rtt();
275 }
276
277 void
uart_init(uint32_t pclk)278 uart_init(uint32_t pclk)
279 {
280 /* Setup UART0 clocking: Use PCLK */
281 *(volatile uint32_t*)(S3C2440_UART_BASE(0)+SSCOM_UBRDIV) =
282 (pclk/(UART_BAUDRATE*16)) - 1;
283
284 *(volatile uint32_t*)(S3C2440_UART_BASE(0)+SSCOM_UCON) =
285 UCON_TXMODE_INT | UCON_RXMODE_INT;
286
287 *(volatile uint32_t*)(S3C2440_UART_BASE(0)+SSCOM_ULCON) =
288 ULCON_PARITY_NONE | ULCON_LENGTH_8;
289
290 *(volatile uint32_t*)(S3C2440_UART_BASE(0)+SSCOM_UFCON) =
291 UFCON_TXTRIGGER_0 | UFCON_TXFIFO_RESET | UFCON_FIFO_ENABLE;
292 }
293
294 static uint32_t countdown_duration;
295
296 static
time_init(uint32_t pclk)297 void time_init(uint32_t pclk)
298 {
299 /* Configure timer0 to be as slow as possible:
300 Prescaler = 255
301 Divider = 16
302 */
303
304 /* First, configure the prescaler */
305 *(volatile uint32_t*)(S3C2440_TIMER_BASE+TIMER_TCFG0) = 0xff;
306
307 /* Next, the divider */
308 *(volatile uint32_t*)(S3C2440_TIMER_BASE+TIMER_TCFG1) |=
309 (TCFG1_MUX_DIV16 <<TCFG1_MUX_SHIFT(0)) & TCFG1_MUX_MASK(0);
310
311 *(volatile uint32_t*)(S3C2440_TIMER_BASE+TIMER_TCON) =
312 TCON_MANUALUPDATE(0);
313 *(volatile uint32_t*)(S3C2440_TIMER_BASE+TIMER_TCNTB(0)) =
314 0xffff;
315 *(volatile uint32_t*)(S3C2440_TIMER_BASE+TIMER_TCON) =
316 TCON_START(0);
317
318
319 /* Timer count down duration */
320 countdown_duration = 65535/(pclk/256/16);
321 timer_inc_rate = pclk/256/16;
322 // printf("Countdown duration is: %ds\n", countdown_duration);
323 #if 0
324 {
325 /* Timer test */
326 time_t time, old_time;
327
328 while(1) {
329 time = old_time = getsecs();
330 do {
331 time = getsecs();
332 } while(time == old_time);
333 printf("Count %u\n", (int)time);
334 }
335 }
336 #endif
337 }
338
339 time_t
getsecs()340 getsecs()
341 {
342 time_t secs = getusecs()/1000000;
343 return secs;
344 }
345
346 time_t
getusecs()347 getusecs() {
348 uint32_t count;
349 //do {
350 count = *(volatile uint32_t*)(S3C2440_TIMER_BASE+TIMER_TCNTO(0));
351 //} while( count > 65500);
352
353 *(volatile uint32_t*)(S3C2440_TIMER_BASE+TIMER_TCON) =
354 TCON_MANUALUPDATE(0);
355 *(volatile uint32_t*)(S3C2440_TIMER_BASE+TIMER_TCNTB(0)) =
356 0xffff;
357 *(volatile uint32_t*)(S3C2440_TIMER_BASE+TIMER_TCON) =
358 TCON_START(0);
359
360 wallclock += ((65535-count)*1000000) / timer_inc_rate;
361
362 return wallclock;
363 }
364
365 void
usleep(int us)366 usleep(int us) {
367 uint32_t count;
368 uint32_t target_clock = wallclock+us;
369
370 while( wallclock < target_clock) {
371 do {
372 count = *(volatile uint32_t*)(S3C2440_TIMER_BASE+TIMER_TCNTO(0));
373 } while( count > 65500);
374
375 *(volatile uint32_t*)(S3C2440_TIMER_BASE+TIMER_TCON) =
376 TCON_MANUALUPDATE(0);
377 *(volatile uint32_t*)(S3C2440_TIMER_BASE+TIMER_TCNTB(0)) =
378 0xffff;
379 *(volatile uint32_t*)(S3C2440_TIMER_BASE+TIMER_TCON) =
380 TCON_START(0);
381
382 wallclock += ((65535-count)*1000000) / timer_inc_rate;
383 }
384 }
385
386
387 void
mini2440_panic()388 mini2440_panic()
389 {
390 int i, l;
391 int v;
392 while(1) {
393 CLEAR_LEDS();
394 for(l=0; l<0xffffff; l++) {
395 v = *((int*)(S3C2440_TIMER_BASE+TIMER_TCNTO(0)));
396 }
397 for(i=1; i<=4; i++) {
398 LED_ON(i);
399 }
400 for(l=0; l<0xffffff; l++) {
401 v = *((int*)(S3C2440_TIMER_BASE+TIMER_TCNTO(0)));
402 }
403 __USE(v);
404 }
405 }
406
407 void
s3c24x0_clock_freq2(vaddr_t clkman_base,int * fclk,int * hclk,int * pclk)408 s3c24x0_clock_freq2(vaddr_t clkman_base, int *fclk, int *hclk, int *pclk)
409 {
410 uint32_t pllcon, divn, camdivn;
411 int mdiv, pdiv, sdiv;
412 uint32_t f, h, p;
413
414 pllcon = *(volatile uint32_t *)(clkman_base + CLKMAN_MPLLCON);
415 divn = *(volatile uint32_t *)(clkman_base + CLKMAN_CLKDIVN);
416 camdivn = *(volatile uint32_t *)(clkman_base + CLKMAN_CAMDIVN);
417
418 mdiv = (pllcon & PLLCON_MDIV_MASK) >> PLLCON_MDIV_SHIFT;
419 pdiv = (pllcon & PLLCON_PDIV_MASK) >> PLLCON_PDIV_SHIFT;
420 sdiv = (pllcon & PLLCON_SDIV_MASK) >> PLLCON_SDIV_SHIFT;
421
422 f = ((mdiv + 8) * S3C2XX0_XTAL_CLK) / ((pdiv + 2) * (1 << sdiv)) * 2;
423 h = f;
424
425 /* HDIVN of CLKDIVN can have 4 distinct values */
426 switch( (divn & CLKDIVN_HDIVN_MASK) >> CLKDIVN_HDIVN_SHIFT )
427 {
428 case 0:
429 /* 00b: HCLK = FCLK/1*/
430 break;
431 case 1:
432 /* 01b: HCLK = FCLK/2*/
433 h /= 2;
434 break;
435 case 2:
436 /* 10b: HCLK = FCLK/4 when CAMDIVN[9] (HCLK4_HALF) = 0
437 * HCLK = FCLK/8 when CAMDIVN[9] (HCLK4_HALF) = 1 */
438 if( camdivn & CLKCAMDIVN_HCLK4_HALF )
439 h /= 8;
440 else
441 h /= 4;
442 break;
443 case 3:
444 /* 11b: HCLK = FCLK/3 when CAMDIVN[8] (HCLK3_HALF) = 0
445 * HCLK = FCLK/6 when CAMDIVN[8] (HCLK3_HALF) = 1 */
446 if( camdivn & CLKCAMDIVN_HCLK3_HALF )
447 h /= 6;
448 else
449 h /= 3;
450 break;
451 }
452
453 p = h;
454
455 if (divn & CLKDIVN_PDIVN)
456 p /= 2;
457
458 if (fclk) *fclk = f;
459 if (hclk) *hclk = h;
460 if (pclk) *pclk = p;
461 }
462
463 void
putchar(int c)464 putchar(int c)
465 {
466 uint32_t stat;
467
468 if (c == '\n')
469 putchar('\r');
470
471 do {
472 stat = CSR_READ(S3C2440_UART_BASE(0) + SSCOM_UTRSTAT);
473 } while ((stat & UTRSTAT_TXEMPTY) == 0);
474
475 CSR_WRITE(S3C2440_UART_BASE(0) + SSCOM_UTXH, c);
476 }
477
478 void
_rtt()479 _rtt()
480 {
481 int cpsr_save, tmp;
482 /* Disable interrupts */
483 __asm volatile("mrs %0, cpsr;"
484 "orr %1, %0, %2;"
485 "msr cpsr_c, %1;"
486 : "=r" (cpsr_save), "=r" (tmp)
487 : "I" (I32_bit)
488 );
489
490 /* Disable MMU */
491 __asm volatile("mrc p15, 0, %0, c1, c0, 0;"
492 "bic %0, %0, %1;"
493 "mcr p15, 0, %0, c1, c0, 0;"
494 : "=r" (tmp)
495 : "I" (CPU_CONTROL_MMU_ENABLE)
496 );
497
498 /* Configure watchdog to fire now */
499 *(volatile uint32_t *)(S3C2440_WDT_BASE + WDT_WTCON) =
500 (0 << WTCON_PRESCALE_SHIFT) | WTCON_ENABLE |
501 WTCON_CLKSEL_16 | WTCON_ENRST;
502 __builtin_unreachable();
503 }
504
505 void
bi_init(void * addr)506 bi_init(void *addr)
507 {
508 struct btinfo_magic bi_magic;
509
510 memset(addr, 0, BOOTINFO_MAXSIZE);
511 bi_next = (char*) addr;
512 bi_size = 0;
513
514 bi_magic.magic = BOOTINFO_MAGIC;
515 bi_add(&bi_magic, BTINFO_MAGIC, sizeof(bi_magic));
516 }
517
518
519 void
bi_add(void * new,int type,int size)520 bi_add(void *new, int type, int size)
521 {
522 struct btinfo_common *bi;
523
524 if (bi_size + size > BOOTINFO_MAXSIZE)
525 return;
526
527 bi = new;
528 bi->next = size;
529 bi->type = type;
530 memcpy(bi_next, new, size);
531 bi_next += size;
532 }
533
534 static void
parse_mac_address(const char * str,uint8_t * enaddr)535 parse_mac_address(const char *str, uint8_t *enaddr)
536 {
537 int i;
538 char *next = (char*)str;
539
540 for(i=0;i<6;i++) {
541 str = next;
542 enaddr[i] = (unsigned char)strtoll(str, &next, 16);
543 if( *next == ':' ) {
544 next++;
545 } else {
546 break;
547 }
548 }
549 }
550
551 static void
brdsetup(void)552 brdsetup(void)
553 {
554 /*
555 * MINI2440 pin usage summary
556 *
557 * B5 output LED1 control
558 * B6 output LED2 control
559 * B7 output LED3 control
560 * B8 output LED4 control
561 * G0 EINT8 K1 button
562 * G3 EINT11 K2 button
563 * G5 EINT13 K3 button
564 * G6 EINT14 K4 button
565 * G7 EINT15 K5 button
566 * G11 EINT19 K6 button
567 * F7 EINT7 DM9000 interrupt
568 * G12 EINT20 camera interrupt
569 * G8 input SD card presense detect
570 * H8 input SD write protect sense
571 * B0 TOUT0 buzzer PWM
572 * B1 TOUT1 LCD backlight PWM
573 * B2 output UDA1341 audio L3MODE
574 * B3 output UDA1341 audio L3DATA
575 * B4 output UDA1341 audio L3LOCK
576 *
577 * A21, A11, G15, G14, G13: not used.
578 *
579 * i input sense
580 * o output control
581 * 2 function 2
582 * 3 function 3
583 * 0 output control (A only)
584 * 1 function 1 (A only)
585 * ./x no function, not connected or don't-care
586 *
587 * A ........ .1x11111 1111x111 11111111
588 * B .....22o ooooooo2
589 * C 22222222 22222222
590 * D 22222222 22222222
591 * E 22222222 22222222
592 * F ........ 22222222
593 * G xxx2222i 22232322
594 * H .....22i 22222222
595 * J ...22222 22222222
596 */
597 iomux('A', "........ .1x11111 1111x111 11111111");
598 iomux('B', ".....22o ooooooo2");
599 iomux('C', "22222222 22222222");
600 iomux('D', "22222222 22222222");
601 iomux('E', "22222222 22222222");
602 iomux('F', "........ 22222222");
603 iomux('G', "xxx2222i 22232322");
604 iomux('H', ".....22i 22222222");
605 iomux('J', "...22222 22222222");
606
607 /* mask all possible external interrupt source [23:3] */
608 CSR_WRITE(S3C2440_GPIO_BASE + GPIO_EINTMASK, ~0);
609 }
610
611 static void
iomux(int grp,const char * cnf)612 iomux(int grp, const char *cnf)
613 {
614 uint32_t con;
615 int sft, i, v;
616
617 con = v = 0;
618 sft = (grp != 'A') ? 2 : 1;
619 for (i = 0; cnf[i] != '\0'; i++) {
620 switch (cnf[i]) {
621 case 'i':
622 case '0':
623 case '.':
624 case 'x':
625 v = 0; break;
626 case 'o':
627 case '1':
628 v = 1; break;
629 case '2':
630 v = 2; break;
631 case '3':
632 v = 3; break;
633 default:
634 continue;
635 }
636 con = (con << sft) | v;
637 }
638 CSR_WRITE(S3C2440_GPIO_BASE + 0x10 * (grp - 'A'), con);
639 }
640