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