Home | History | Annotate | Line # | Download | only in boot2440
      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[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
    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
    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
    340 getsecs()
    341 {
    342 	time_t secs = getusecs()/1000000;
    343 	return secs;
    344 }
    345 
    346 time_t
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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