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