Home | History | Annotate | Line # | Download | only in boot2440
main.c revision 1.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 
     88 extern void* dm9k_init(unsigned int tag, void *macaddr);
     89 
     90 /* External variables */
     91 extern char bootprog_name[], bootprog_rev[];
     92 
     93 /* External functions */
     94 extern void netif_match(unsigned int tag, uint8_t *macaddr);
     95 /*  extern int sdif_init(unsigned int tag);*/
     96 
     97 /* Global variables */
     98 int pclk;
     99 struct btinfo_rootdevice	bi_rdev;
    100 
    101 /* This is not very flexible, as only one net device is allowed */
    102 struct btinfo_net		bi_net;
    103 
    104 struct btinfo_bootpath		bi_path;
    105 
    106 void
    107 main(int argc, char *argv[])
    108 {
    109 	int fclk, hclk;
    110 	int fd;
    111 	unsigned long marks[MARK_MAX];
    112 	unsigned char hdr[0x26];
    113 	void (*entry)(void*);
    114 	unsigned elfpriv;
    115 	char *bootfile;
    116 	char *bf;
    117 	bool kernel_loaded;
    118 	uint8_t enaddr[6] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
    119 
    120 	/* Give some indication that main() has been reached */
    121 	CLEAR_LEDS();
    122 	LED_ON(4);
    123 
    124 	/* Next, we setup the clock of the S3C2440 such that we are not
    125 	   dependent on any other bootloader in this regard.
    126 	   Target FCLK is 405MHz, and we assume an input crystal of 12MHz
    127 	*/
    128 	*(volatile uint32_t*)(S3C2440_CLKMAN_BASE+CLKMAN_MPLLCON) =
    129 		((0x7F << PLLCON_MDIV_SHIFT) & PLLCON_MDIV_MASK) |
    130 		((2 << PLLCON_PDIV_SHIFT) & PLLCON_PDIV_MASK) |
    131 		((1 << PLLCON_SDIV_SHIFT) & PLLCON_SDIV_MASK);
    132 	*(volatile uint32_t*)(S3C2440_CLKMAN_BASE+CLKMAN_UPLLCON) =
    133 		((0x38 << PLLCON_MDIV_SHIFT) & PLLCON_MDIV_MASK) |
    134 		((2 << PLLCON_PDIV_SHIFT) & PLLCON_PDIV_MASK) |
    135 		((2 << PLLCON_SDIV_SHIFT) & PLLCON_SDIV_MASK);
    136 
    137 	LED_ON(1);
    138 
    139 	s3c24x0_clock_freq2(S3C2440_CLKMAN_BASE, &fclk, &hclk, &pclk);
    140 
    141 	uart_init(pclk);
    142 	time_init(pclk);
    143 
    144 	/* Let the user know we are alive */
    145 	printf("\n");
    146 	printf(">> %s boot2440, revision %s\n", bootprog_name, bootprog_rev);
    147 
    148 	bootinfo = (void*) BOOTINFO_ADDR;
    149 	bi_init(bootinfo);
    150 
    151 	bi_net.devname[0] = 0;
    152 	bi_path.bootpath[0] = 0;
    153 
    154 	/* Try to get boot arguments from any previous boot-loader */
    155 	{
    156 		struct btinfo_bootstring ba;
    157 		int j, i;
    158 
    159 		printf("Argument count: %d\n", argc);
    160 
    161 		j = 0;
    162 		for (i = 0; i < argc; i++) {
    163 			if (j == MAX_BOOT_STRING-1) {
    164 				ba.bootstring[j] = '\0';
    165 				continue;
    166 			}
    167 			if (strncmp(argv[i], "mac=", 4) == 0) {
    168 				parse_mac_address(argv[i]+4, enaddr);
    169 			} else {
    170 				if (j != 0)
    171 					ba.bootstring[j++] = ' ';
    172 
    173 				strncpy(ba.bootstring+j, argv[i], MAX_BOOT_STRING-j);
    174 				j += strlen(argv[i]);
    175 			}
    176 		}
    177 
    178 		printf("Boot string: %s\n", ba.bootstring);
    179 
    180 		bi_add(&ba, BTINFO_BOOTSTRING, sizeof(ba));
    181 	}
    182 
    183 	LED_ON(3);
    184 
    185 	if (argc > 1) {
    186 		bf = argv[argc-1];
    187 	} else {
    188 		bf = default_boot;
    189 	}
    190 
    191 	/* Detect networking devices */
    192 	netif_match(0, enaddr);
    193 
    194 	kernel_loaded = FALSE;
    195 	do {
    196 		bootfile = strsep(&bf, ";");
    197 		printf("Trying \"%s\"...\n", bootfile);
    198 		fd = open(bootfile, 0);
    199 		if (fd < 0) {
    200 			printf("Failed: %d\n", errno);
    201 			close(fd);
    202 			continue;
    203 		}
    204 
    205 		if (fdloadfile(fd, marks, LOAD_ALL) == 0) {
    206 			kernel_loaded = TRUE;
    207 			break;
    208 		}
    209 	} while(bf != NULL);
    210 
    211 	if (!kernel_loaded) {
    212 		panic("Failed to load kernel\n");
    213 		_rtt();
    214 	}
    215 
    216 #if 1
    217 	/* Set MAC address of the 'dme' net device, if
    218 	 * it isn't set already */
    219 	if (bi_net.devname[0] == 0) {
    220 		uint8_t en[6] = {DM9000MAC};
    221 		snprintf(bi_net.devname, sizeof(bi_net.devname), "dme");
    222 		bi_net.cookie = 0;
    223 
    224 		memcpy(bi_net.mac_address, en, sizeof(bi_net.mac_address));
    225 	}
    226 #endif
    227 	/*
    228 	 * ARM ELF header has a distinctive value in "private flags"
    229 	 * field of offset [0x24:25];
    230 	 * - NetBSD 02 06
    231 	 * - Linux  02 00 (2.4) or 02 02 (2.6)
    232 	 */
    233 	lseek(fd, (off_t)0, SEEK_SET);
    234 	read(fd, &hdr, sizeof(hdr));
    235 	elfpriv = *(unsigned short *)&hdr[0x24];
    236 
    237 	entry = (void *)marks[MARK_ENTRY];
    238 	if (elfpriv == 0x0602) {
    239 		struct btinfo_symtab bi_syms;
    240 
    241 		bi_syms.nsym = marks[MARK_NSYM];
    242 		bi_syms.ssym = (void*)marks[MARK_SYM];
    243 		bi_syms.esym = (void*)marks[MARK_END];
    244 		bi_add(&bi_syms, BTINFO_SYMTAB, sizeof(bi_syms));
    245 		if (bi_path.bootpath[0] != 0)
    246 		  bi_add(&bi_path, BTINFO_BOOTPATH, sizeof(bi_path));
    247 		bi_add(&bi_rdev, BTINFO_ROOTDEVICE, sizeof(bi_rdev));
    248 		if (bi_net.devname[0] != 0 )
    249 			bi_add(&bi_net, BTINFO_NET, sizeof(bi_net));
    250 	} else {
    251 		printf("Loaded object is not NetBSD ARM ELF");
    252 		_rtt();
    253 	}
    254 
    255 	printf("entry=%p, nsym=%lu, ssym=%p, esym=%p\n",
    256 	       (void *)marks[MARK_ENTRY],
    257 	       marks[MARK_NSYM],
    258 	       (void *)marks[MARK_SYM],
    259 	       (void *)marks[MARK_END]);
    260 	(*entry)(bootinfo);
    261 
    262 	printf("exec returned, restarting...\n");
    263 	_rtt();
    264 }
    265 
    266 void
    267 uart_init(uint32_t pclk)
    268 {
    269 	/* Setup UART0 clocking: Use PCLK */
    270 	*(volatile uint32_t*)(S3C2440_UART_BASE(0)+SSCOM_UBRDIV) =
    271 		(pclk/(UART_BAUDRATE*16)) - 1;
    272 
    273 	*(volatile uint32_t*)(S3C2440_UART_BASE(0)+SSCOM_UCON) =
    274 		UCON_TXMODE_INT | UCON_RXMODE_INT;
    275 
    276 	*(volatile uint32_t*)(S3C2440_UART_BASE(0)+SSCOM_ULCON) =
    277 		ULCON_PARITY_NONE | ULCON_LENGTH_8;
    278 
    279 	*(volatile uint32_t*)(S3C2440_UART_BASE(0)+SSCOM_UFCON) =
    280 		UFCON_TXTRIGGER_0 | UFCON_TXFIFO_RESET | UFCON_FIFO_ENABLE;
    281 }
    282 
    283 static uint32_t countdown_duration;
    284 
    285 static
    286 void time_init(uint32_t pclk)
    287 {
    288 	/* Configure timer0 to be as slow as possible:
    289 	   Prescaler = 255
    290 	   Divider = 16
    291 	 */
    292 
    293 	/* First, configure the prescaler */
    294 	*(volatile uint32_t*)(S3C2440_TIMER_BASE+TIMER_TCFG0) = 0xff;
    295 
    296 	/* Next, the divider */
    297 	*(volatile uint32_t*)(S3C2440_TIMER_BASE+TIMER_TCFG1) |=
    298 		(TCFG1_MUX_DIV16 <<TCFG1_MUX_SHIFT(0)) & TCFG1_MUX_MASK(0);
    299 
    300 		*(volatile uint32_t*)(S3C2440_TIMER_BASE+TIMER_TCON) =
    301 			TCON_MANUALUPDATE(0);
    302 		*(volatile uint32_t*)(S3C2440_TIMER_BASE+TIMER_TCNTB(0)) =
    303 			0xffff;
    304 		*(volatile uint32_t*)(S3C2440_TIMER_BASE+TIMER_TCON) =
    305 			TCON_START(0);
    306 
    307 
    308 	/* Timer count down duration */
    309 	countdown_duration = 65535/(pclk/256/16);
    310 	timer_inc_rate = pclk/256/16;
    311 	//	printf("Countdown duration is: %ds\n", countdown_duration);
    312 #if 0
    313 	{
    314 		/* Timer test */
    315 		time_t time, old_time;
    316 
    317 		while(1) {
    318 			time = old_time = getsecs();
    319 			do {
    320 				time = getsecs();
    321 			} while(time == old_time);
    322 			printf("Count %u\n", (int)time);
    323 		}
    324 	}
    325 #endif
    326 }
    327 
    328 time_t
    329 getsecs()
    330 {
    331 	time_t secs = getusecs()/1000000;
    332 	return secs;
    333 }
    334 
    335 time_t
    336 getusecs() {
    337 	uint32_t count;
    338 	//do {
    339 		count = *(volatile uint32_t*)(S3C2440_TIMER_BASE+TIMER_TCNTO(0));
    340 //} while( count > 65500);
    341 
    342 	*(volatile uint32_t*)(S3C2440_TIMER_BASE+TIMER_TCON) =
    343 		TCON_MANUALUPDATE(0);
    344 	*(volatile uint32_t*)(S3C2440_TIMER_BASE+TIMER_TCNTB(0)) =
    345 		0xffff;
    346 	*(volatile uint32_t*)(S3C2440_TIMER_BASE+TIMER_TCON) =
    347 		TCON_START(0);
    348 
    349 	wallclock += ((65535-count)*1000000) / timer_inc_rate;
    350 
    351 	return wallclock;
    352 }
    353 
    354 void
    355 usleep(int us) {
    356 	uint32_t count;
    357 	uint32_t target_clock = wallclock+us;
    358 
    359 	while( wallclock < target_clock) {
    360 		do {
    361 			count = *(volatile uint32_t*)(S3C2440_TIMER_BASE+TIMER_TCNTO(0));
    362 		} while( count > 65500);
    363 
    364 		*(volatile uint32_t*)(S3C2440_TIMER_BASE+TIMER_TCON) =
    365 			TCON_MANUALUPDATE(0);
    366 		*(volatile uint32_t*)(S3C2440_TIMER_BASE+TIMER_TCNTB(0)) =
    367 			0xffff;
    368 		*(volatile uint32_t*)(S3C2440_TIMER_BASE+TIMER_TCON) =
    369 			TCON_START(0);
    370 
    371 		wallclock += ((65535-count)*1000000) / timer_inc_rate;
    372 	}
    373 }
    374 
    375 
    376 void
    377 mini2440_panic()
    378 {
    379 	int i, l;
    380 	int v;
    381 	while(1) {
    382 		CLEAR_LEDS();
    383 		for(l=0; l<0xffffff; l++) {
    384 			v = *((int*)(S3C2440_TIMER_BASE+TIMER_TCNTO(0)));
    385 		}
    386 		for(i=1; i<=4; i++) {
    387 			LED_ON(i);
    388 		}
    389 		for(l=0; l<0xffffff; l++) {
    390 			v = *((int*)(S3C2440_TIMER_BASE+TIMER_TCNTO(0)));
    391 		}
    392 	}
    393 }
    394 
    395 void
    396 s3c24x0_clock_freq2(vaddr_t clkman_base, int *fclk, int *hclk, int *pclk)
    397 {
    398 	uint32_t pllcon, divn, camdivn;
    399 	int mdiv, pdiv, sdiv;
    400 	uint32_t f, h, p;
    401 
    402 	pllcon = *(volatile uint32_t *)(clkman_base + CLKMAN_MPLLCON);
    403 	divn = *(volatile uint32_t *)(clkman_base + CLKMAN_CLKDIVN);
    404 	camdivn = *(volatile uint32_t *)(clkman_base + CLKMAN_CAMDIVN);
    405 
    406 	mdiv = (pllcon & PLLCON_MDIV_MASK) >> PLLCON_MDIV_SHIFT;
    407 	pdiv = (pllcon & PLLCON_PDIV_MASK) >> PLLCON_PDIV_SHIFT;
    408 	sdiv = (pllcon & PLLCON_SDIV_MASK) >> PLLCON_SDIV_SHIFT;
    409 
    410 	f = ((mdiv + 8) * S3C2XX0_XTAL_CLK) / ((pdiv + 2) * (1 << sdiv)) * 2;
    411 	h = f;
    412 
    413 	/* HDIVN of CLKDIVN can have 4 distinct values */
    414 	switch( (divn & CLKDIVN_HDIVN_MASK) >> CLKDIVN_HDIVN_SHIFT )
    415 		{
    416 		case 0:
    417 			/* 00b: HCLK = FCLK/1*/
    418 			break;
    419 		case 1:
    420 			/* 01b: HCLK = FCLK/2*/
    421 			h /= 2;
    422 			break;
    423 		case 2:
    424 			/* 10b: HCLK = FCLK/4 when CAMDIVN[9] (HCLK4_HALF) = 0
    425 			 *      HCLK = FCLK/8 when CAMDIVN[9] (HCLK4_HALF) = 1 */
    426 			if( camdivn & CLKCAMDIVN_HCLK4_HALF )
    427 				h /= 8;
    428 			else
    429 				h /= 4;
    430 			break;
    431 		case 3:
    432 			/* 11b: HCLK = FCLK/3 when CAMDIVN[8] (HCLK3_HALF) = 0
    433 			 *      HCLK = FCLK/6 when CAMDIVN[8] (HCLK3_HALF) = 1 */
    434 			if( camdivn & CLKCAMDIVN_HCLK3_HALF )
    435 				h /= 6;
    436 			else
    437 				h /= 3;
    438 			break;
    439 		}
    440 
    441 	p = h;
    442 
    443 	if (divn & CLKDIVN_PDIVN)
    444 		p /= 2;
    445 
    446 	if (fclk) *fclk = f;
    447 	if (hclk) *hclk = h;
    448 	if (pclk) *pclk = p;
    449 }
    450 
    451 void
    452 putchar(int c)
    453 {
    454 	uint32_t stat;
    455 
    456 	if (c == '\n')
    457 		putchar('\r');
    458 
    459 	do {
    460 		stat = CSR_READ(S3C2440_UART_BASE(0) + SSCOM_UTRSTAT);
    461 	} while ((stat & UTRSTAT_TXEMPTY) == 0);
    462 
    463 	CSR_WRITE(S3C2440_UART_BASE(0) + SSCOM_UTXH, c);
    464 }
    465 
    466 void
    467 _rtt()
    468 {
    469 	int cpsr_save, tmp;
    470 	/* Disable interrupts */
    471 	__asm volatile("mrs %0, cpsr;"
    472 		       "orr %1, %0, %2;"
    473 		       "msr cpsr_c, %1;"
    474 		       : "=r" (cpsr_save), "=r" (tmp)
    475 		       : "I" (I32_bit)
    476 		       );
    477 
    478 	/* Disable MMU */
    479 	__asm volatile("mrc p15, 0, %0, c1, c0, 0;"
    480 		       "bic %0, %0, %1;"
    481 		       "mcr p15, 0, %0, c1, c0, 0;"
    482 		       : "=r" (tmp)
    483 		       : "I" (CPU_CONTROL_MMU_ENABLE)
    484 		       );
    485 
    486 	/* Configure watchdog to fire now */
    487 	*(volatile uint32_t *)(S3C2440_WDT_BASE + WDT_WTCON) =
    488 		(0 << WTCON_PRESCALE_SHIFT) | WTCON_ENABLE |
    489 		WTCON_CLKSEL_16 | WTCON_ENRST;
    490 }
    491 
    492 void
    493 bi_init(void *addr)
    494 {
    495 	struct btinfo_magic bi_magic;
    496 
    497 	memset(addr, 0, BOOTINFO_MAXSIZE);
    498 	bi_next = (char*) addr;
    499 	bi_size = 0;
    500 
    501 	bi_magic.magic = BOOTINFO_MAGIC;
    502 	bi_add(&bi_magic, BTINFO_MAGIC, sizeof(bi_magic));
    503 }
    504 
    505 
    506 void
    507 bi_add(void *new, int type, int size)
    508 {
    509 	struct btinfo_common *bi;
    510 
    511 	if (bi_size + size > BOOTINFO_MAXSIZE)
    512 		return;
    513 
    514 	bi = new;
    515 	bi->next = size;
    516 	bi->type = type;
    517 	memcpy(bi_next, new, size);
    518 	bi_next += size;
    519 }
    520 
    521 static void
    522 parse_mac_address(const char *str, uint8_t *enaddr)
    523 {
    524 	int i;
    525 	char *next = (char*)str;
    526 
    527 	for(i=0;i<6;i++) {
    528 		str = next;
    529 		enaddr[i] = (unsigned char)strtoll(str, &next, 16);
    530 		if( *next == ':' ) {
    531 			next++;
    532 		} else {
    533 			break;
    534 		}
    535 	}
    536 }
    537