xref: /netbsd-src/sys/arch/evbarm/stand/boot2440/main.c (revision 1bf05dde35db924a668718afee162ac1941f92f1)
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