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 */
61static time_t	wallclock = 0;
62static uint32_t timer_inc_rate;
63void *bootinfo;
64int bi_size;
65char *bi_next;
66
67#define STR_EXPAND(tok) #tok
68#define STR(tok) STR_EXPAND(tok)
69
70#if defined(DEFAULT_BOOTFILE)
71static char *default_boot=STR(DEFAULT_BOOTFILE);
72#else
73static char *default_boot="net:";
74#endif
75
76time_t getsecs();
77time_t getusecs();
78
79/* Local functions */
80static void s3c24x0_clock_freq2(vaddr_t clkman_base, int *fclk, int *hclk,
81				int *pclk);
82static void uart_init(uint32_t pclk);
83static void time_init(uint32_t pclk);
84static void bi_init(void *addr);
85static void bi_add(void *new, int type, int size);
86static void parse_mac_address(const char *str, uint8_t *enaddr);
87static void brdsetup(void);
88static void iomux(int, const char *);
89
90extern void* dm9k_init(unsigned int tag, void *macaddr);
91
92/* External variables */
93extern char bootprog_name[], bootprog_rev[];
94
95/* External functions */
96extern void netif_match(unsigned int tag, uint8_t *macaddr);
97/*  extern int sdif_init(unsigned int tag);*/
98
99/* Global variables */
100uint32_t socmodel;
101int pclk;
102struct btinfo_rootdevice	bi_rdev;
103
104/* This is not very flexible, as only one net device is allowed */
105struct btinfo_net		bi_net;
106
107struct btinfo_bootpath		bi_path;
108
109void
110main(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
277void
278uart_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
294static uint32_t countdown_duration;
295
296static
297void 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
339time_t
340getsecs()
341{
342	time_t secs = getusecs()/1000000;
343	return secs;
344}
345
346time_t
347getusecs() {
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
365void
366usleep(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
387void
388mini2440_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
407void
408s3c24x0_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
463void
464putchar(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
478void
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
505void
506bi_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
519void
520bi_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
534static void
535parse_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
551static void
552brdsetup(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
611static void
612iomux(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