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