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 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 elfpriv = *(unsigned short *)&hdr[0x24]; 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 } 403 } 404 405 void 406 s3c24x0_clock_freq2(vaddr_t clkman_base, int *fclk, int *hclk, int *pclk) 407 { 408 uint32_t pllcon, divn, camdivn; 409 int mdiv, pdiv, sdiv; 410 uint32_t f, h, p; 411 412 pllcon = *(volatile uint32_t *)(clkman_base + CLKMAN_MPLLCON); 413 divn = *(volatile uint32_t *)(clkman_base + CLKMAN_CLKDIVN); 414 camdivn = *(volatile uint32_t *)(clkman_base + CLKMAN_CAMDIVN); 415 416 mdiv = (pllcon & PLLCON_MDIV_MASK) >> PLLCON_MDIV_SHIFT; 417 pdiv = (pllcon & PLLCON_PDIV_MASK) >> PLLCON_PDIV_SHIFT; 418 sdiv = (pllcon & PLLCON_SDIV_MASK) >> PLLCON_SDIV_SHIFT; 419 420 f = ((mdiv + 8) * S3C2XX0_XTAL_CLK) / ((pdiv + 2) * (1 << sdiv)) * 2; 421 h = f; 422 423 /* HDIVN of CLKDIVN can have 4 distinct values */ 424 switch( (divn & CLKDIVN_HDIVN_MASK) >> CLKDIVN_HDIVN_SHIFT ) 425 { 426 case 0: 427 /* 00b: HCLK = FCLK/1*/ 428 break; 429 case 1: 430 /* 01b: HCLK = FCLK/2*/ 431 h /= 2; 432 break; 433 case 2: 434 /* 10b: HCLK = FCLK/4 when CAMDIVN[9] (HCLK4_HALF) = 0 435 * HCLK = FCLK/8 when CAMDIVN[9] (HCLK4_HALF) = 1 */ 436 if( camdivn & CLKCAMDIVN_HCLK4_HALF ) 437 h /= 8; 438 else 439 h /= 4; 440 break; 441 case 3: 442 /* 11b: HCLK = FCLK/3 when CAMDIVN[8] (HCLK3_HALF) = 0 443 * HCLK = FCLK/6 when CAMDIVN[8] (HCLK3_HALF) = 1 */ 444 if( camdivn & CLKCAMDIVN_HCLK3_HALF ) 445 h /= 6; 446 else 447 h /= 3; 448 break; 449 } 450 451 p = h; 452 453 if (divn & CLKDIVN_PDIVN) 454 p /= 2; 455 456 if (fclk) *fclk = f; 457 if (hclk) *hclk = h; 458 if (pclk) *pclk = p; 459 } 460 461 void 462 putchar(int c) 463 { 464 uint32_t stat; 465 466 if (c == '\n') 467 putchar('\r'); 468 469 do { 470 stat = CSR_READ(S3C2440_UART_BASE(0) + SSCOM_UTRSTAT); 471 } while ((stat & UTRSTAT_TXEMPTY) == 0); 472 473 CSR_WRITE(S3C2440_UART_BASE(0) + SSCOM_UTXH, c); 474 } 475 476 void 477 _rtt() 478 { 479 int cpsr_save, tmp; 480 /* Disable interrupts */ 481 __asm volatile("mrs %0, cpsr;" 482 "orr %1, %0, %2;" 483 "msr cpsr_c, %1;" 484 : "=r" (cpsr_save), "=r" (tmp) 485 : "I" (I32_bit) 486 ); 487 488 /* Disable MMU */ 489 __asm volatile("mrc p15, 0, %0, c1, c0, 0;" 490 "bic %0, %0, %1;" 491 "mcr p15, 0, %0, c1, c0, 0;" 492 : "=r" (tmp) 493 : "I" (CPU_CONTROL_MMU_ENABLE) 494 ); 495 496 /* Configure watchdog to fire now */ 497 *(volatile uint32_t *)(S3C2440_WDT_BASE + WDT_WTCON) = 498 (0 << WTCON_PRESCALE_SHIFT) | WTCON_ENABLE | 499 WTCON_CLKSEL_16 | WTCON_ENRST; 500 } 501 502 void 503 bi_init(void *addr) 504 { 505 struct btinfo_magic bi_magic; 506 507 memset(addr, 0, BOOTINFO_MAXSIZE); 508 bi_next = (char*) addr; 509 bi_size = 0; 510 511 bi_magic.magic = BOOTINFO_MAGIC; 512 bi_add(&bi_magic, BTINFO_MAGIC, sizeof(bi_magic)); 513 } 514 515 516 void 517 bi_add(void *new, int type, int size) 518 { 519 struct btinfo_common *bi; 520 521 if (bi_size + size > BOOTINFO_MAXSIZE) 522 return; 523 524 bi = new; 525 bi->next = size; 526 bi->type = type; 527 memcpy(bi_next, new, size); 528 bi_next += size; 529 } 530 531 static void 532 parse_mac_address(const char *str, uint8_t *enaddr) 533 { 534 int i; 535 char *next = (char*)str; 536 537 for(i=0;i<6;i++) { 538 str = next; 539 enaddr[i] = (unsigned char)strtoll(str, &next, 16); 540 if( *next == ':' ) { 541 next++; 542 } else { 543 break; 544 } 545 } 546 } 547 548 static void 549 brdsetup(void) 550 { 551 /* 552 * MINI2440 pin usage summary 553 * 554 * B5 output LED1 control 555 * B6 output LED2 control 556 * B7 output LED3 control 557 * B8 output LED4 control 558 * G0 EINT8 K1 button 559 * G3 EINT11 K2 button 560 * G5 EINT13 K3 button 561 * G6 EINT14 K4 button 562 * G7 EINT15 K5 button 563 * G11 EINT19 K6 button 564 * F7 EINT7 DM9000 interrupt 565 * G12 EINT20 camera interrupt 566 * G8 input SD card presense detect 567 * H8 input SD write protect sense 568 * B0 TOUT0 buzzer PWM 569 * B1 TOUT1 LCD backlight PWM 570 * B2 output UDA1341 audio L3MODE 571 * B3 output UDA1341 audio L3DATA 572 * B4 output UDA1341 audio L3LOCK 573 * 574 * A21, A11, G15, G14, G13: not used. 575 * 576 * i input sense 577 * o output control 578 * 2 function 2 579 * 3 function 3 580 * 0 output control (A only) 581 * 1 function 1 (A only) 582 * ./x no function, not connected or don't-care 583 * 584 * A ........ .1x11111 1111x111 11111111 585 * B .....22o ooooooo2 586 * C 22222222 22222222 587 * D 22222222 22222222 588 * E 22222222 22222222 589 * F ........ 22222222 590 * G xxx2222i 22232322 591 * H .....22i 22222222 592 * J ...22222 22222222 593 */ 594 iomux('A', "........ .1x11111 1111x111 11111111"); 595 iomux('B', ".....22o ooooooo2"); 596 iomux('C', "22222222 22222222"); 597 iomux('D', "22222222 22222222"); 598 iomux('E', "22222222 22222222"); 599 iomux('F', "........ 22222222"); 600 iomux('G', "xxx2222i 22232322"); 601 iomux('H', ".....22i 22222222"); 602 iomux('J', "...22222 22222222"); 603 604 /* mask all possible external interrupt source [23:3] */ 605 CSR_WRITE(S3C2440_GPIO_BASE + GPIO_EINTMASK, ~0); 606 } 607 608 static void 609 iomux(int grp, const char *cnf) 610 { 611 uint32_t con; 612 int sft, i, v; 613 614 con = v = 0; 615 sft = (grp != 'A') ? 2 : 1; 616 for (i = 0; cnf[i] != '\0'; i++) { 617 switch (cnf[i]) { 618 case 'i': 619 case '0': 620 case '.': 621 case 'x': 622 v = 0; break; 623 case 'o': 624 case '1': 625 v = 1; break; 626 case '2': 627 v = 2; break; 628 case '3': 629 v = 3; break; 630 default: 631 continue; 632 } 633 con = (con << sft) | v; 634 } 635 CSR_WRITE(S3C2440_GPIO_BASE + 0x10 * (grp - 'A'), con); 636 } 637