Home | History | Annotate | Line # | Download | only in boot2440
main.c revision 1.2.4.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 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