/**    
    @file     tgpio.c
    @date     2010/1/4
    @author   오재경 freefrug@falinux.com  FALinux.Co.,Ltd.
    @brief    gpio 를 드라이버없이 mmap를 이용하여 제어한다.

              Ver 0.6.0    am3874 포함, GP0테스트 완료 

              Ver 0.5.0    s5pv210 포함, 테스트 완료 
                           pull-up, pull-dn 설정변경
                           
              Ver 0.4.0    tmmap.c 를 사용하지 않고 famap.c 를 사용하는것으로 수정
              Ver 0.3.0    ixp420  테스트 완료
              Ver 0.2.1    s3c6410, pxa270 테스트 완료
              Ver 0.2.0    pxa270  완료
                           pxa255  완료(테스트 안됨)
              Ver 0.1.0    s3c6410 완료
                           s3c2440 완료(테스트 안됨)
    @modify   
    @todo     다음과 같은 MCU 들도 지원하여야 한다.
                ..
    @bug     
    @remark   
    @warning 
*/
//----------------------------------------------------------------------------

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <fcntl.h>
#include <ctype.h>
#include <stdarg.h>
#include <time.h>

#include <util.h>
#include <famap.h>
#include <tgpio.h>
#include <tgpio_imx6q.h>

static const char desc[] = "tgpio ver 0.7.0";


/// am3874 gpio 관리
static am3x_gpio_register_t  am3874_gpio_register[MCU_AM3874_GPIO_GRP_CNT] = {
	[0] = { .phys = MCU_AM3874_GPIO0_PHYS,     
			//            0             		5
		    .pin_ctrl = { 2,8,9,10,11,  		12,13,0,15,16,  	
		    			  156,157,158,159,160,  161,162,163,164,165,  	
		    			  166,167,52,53,54,  	55,56,57,58,59,  		60,61 }, 
		  },
	[1] = { .phys = MCU_AM3874_GPIO1_PHYS, 
		    .pin_ctrl = { 68,69,74,75,76,  		77,80,62,63,64,  	
		    			  65,140,141,0,0,  		121,85,86,87,113,  	
		    			  114,115,116,0,0,  	0,88,0,0,0,  			0,0 }, 
		  },
	[2] = { .phys = MCU_AM3874_GPIO2_PHYS,
		    .pin_ctrl = { 135,136,137,138,139,  0,0,142,143,144,  
		    			  145,146,147,148,149,  150,151,152,153,154,  
		    			  155,179,180,181,188,  189,196,197,204,205,  	206,207 }, 
		  },
	[3] = { .phys = MCU_AM3874_GPIO3_PHYS,
		    .pin_ctrl = { 208,209,210,211,212,  213,214,215,216,217,  
		    			  218,219,220,221,222,  223,224,225,226,227,  
		    			  0,0,0,0,0,  			0,0,0,0,0,  			0,0 }, 
		  },
};

/// s5pv210 gpio 그룹 인덱스
static int s5pv210_reg_idx_table[] = {
//  A   B   C   D   E   F   G   H   I   J
    0,  1,  2,  3,  4,  5,  6,  7,  8,  9,	// GPA0 ... GPJ0
   10, -1, 11, 12, 13, 14, 17, 20, -1, 23,  // GPA1 ... GPJ1
   -1, -1, -1, -1, -1, 15, 18, 21, -1, 24,  // GPA2 ... GPJ2
   -1, -1, -1, -1, -1, 16, 19, 22, -1, 25,  // GPA3 ... GPJ3
   -1, -1, -1, -1, -1, -1, -1, -1, -1, 26   // GPA4 ... GPJ4
};

/// s5pv210 gpio 레지스터 어프셋과 정보
static s3c_gpio_register_t  s5pv210_gpio_register[27] = {
	
	[0]  = {/* GPA0 */	.gpcnt = 8,	.con_mask_bitcnt = 4,	.ofs_con = 0x0, 	.ofs_dat = 0x4,  	.ofs_pup = 0x8,		},
	[10] = {/* GPA1 */	.gpcnt = 4,	.con_mask_bitcnt = 4,	.ofs_con = 0x20, 	.ofs_dat = 0x24, 	.ofs_pup = 0x28,	},
	[1]  = {/* GPB0 */	.gpcnt = 8,	.con_mask_bitcnt = 4,	.ofs_con = 0x40,	.ofs_dat = 0x44, 	.ofs_pup = 0x48,	},
	[2]  = {/* GPC0 */	.gpcnt = 5,	.con_mask_bitcnt = 4,	.ofs_con = 0x60,	.ofs_dat = 0x64, 	.ofs_pup = 0x68,	},
	[11] = {/* GPC1 */	.gpcnt = 5,	.con_mask_bitcnt = 4,	.ofs_con = 0x80,	.ofs_dat = 0x84, 	.ofs_pup = 0x88,	},
	[3]  = {/* GPD0 */	.gpcnt = 4,	.con_mask_bitcnt = 4,	.ofs_con = 0xa0,	.ofs_dat = 0xa4, 	.ofs_pup = 0xa8,	},
	[12] = {/* GPD1 */	.gpcnt = 6,	.con_mask_bitcnt = 4,	.ofs_con = 0xc0,	.ofs_dat = 0xc4, 	.ofs_pup = 0xc8,	},
	[4]  = {/* GPE0 */	.gpcnt = 8,	.con_mask_bitcnt = 4,	.ofs_con = 0xe0,	.ofs_dat = 0xe4, 	.ofs_pup = 0xe8,	},
	[13] = {/* GPE1 */	.gpcnt = 5,	.con_mask_bitcnt = 4,	.ofs_con = 0x100,	.ofs_dat = 0x104,	.ofs_pup = 0x108,	},		    
	[5]  = {/* GPF0 */	.gpcnt = 8,	.con_mask_bitcnt = 4,	.ofs_con = 0x120,	.ofs_dat = 0x124,	.ofs_pup = 0x128,	},
	[14] = {/* GPF1 */	.gpcnt = 8,	.con_mask_bitcnt = 4,	.ofs_con = 0x140,	.ofs_dat = 0x144,	.ofs_pup = 0x148,	},
	[15] = {/* GPF2 */	.gpcnt = 8,	.con_mask_bitcnt = 4,	.ofs_con = 0x160,	.ofs_dat = 0x164,	.ofs_pup = 0x168,	},
	[16] = {/* GPF3 */	.gpcnt = 8,	.con_mask_bitcnt = 4,	.ofs_con = 0x180,	.ofs_dat = 0x184,	.ofs_pup = 0x188,	},		    
	[6]  = {/* GPG0 */	.gpcnt = 7,	.con_mask_bitcnt = 4,	.ofs_con = 0x1a0,	.ofs_dat = 0x1a4,	.ofs_pup = 0x1a8,	},
	[17] = {/* GPG1 */	.gpcnt = 7,	.con_mask_bitcnt = 4,	.ofs_con = 0x1c0,	.ofs_dat = 0x1c4,	.ofs_pup = 0x1c8,	},
	[18] = {/* GPG2 */	.gpcnt = 7,	.con_mask_bitcnt = 4,	.ofs_con = 0x1e0,	.ofs_dat = 0x1e4,	.ofs_pup = 0x1e8,	},
	[19] = {/* GPG3 */	.gpcnt = 7,	.con_mask_bitcnt = 4,	.ofs_con = 0x200,	.ofs_dat = 0x204,	.ofs_pup = 0x208,	},		    
	[7]  = {/* GPH0 */	.gpcnt = 8,	.con_mask_bitcnt = 4,	.ofs_con = 0xc00,	.ofs_dat = 0xc04,	.ofs_pup = 0xc08,	},
	[20] = {/* GPH1 */	.gpcnt = 8,	.con_mask_bitcnt = 4,	.ofs_con = 0xc20,	.ofs_dat = 0xc24,	.ofs_pup = 0xc28,	},
	[21] = {/* GPH2 */	.gpcnt = 8,	.con_mask_bitcnt = 4,	.ofs_con = 0xc40,	.ofs_dat = 0xc44,	.ofs_pup = 0xc48,	},
	[22] = {/* GPH3 */	.gpcnt = 8,	.con_mask_bitcnt = 4,	.ofs_con = 0xc60,	.ofs_dat = 0xc64,	.ofs_pup = 0xc68,	},		    
	[8]  = {/* GPI0 */	.gpcnt = 0,	.con_mask_bitcnt = 4,	.ofs_con = 0x220,	.ofs_dat = 0x224,	.ofs_pup = 0x228,	},		    
	[9]  = {/* GPJ0 */	.gpcnt = 8,	.con_mask_bitcnt = 4,	.ofs_con = 0x240,	.ofs_dat = 0x244,	.ofs_pup = 0x248,	},
	[23] = {/* GPJ1 */	.gpcnt = 6,	.con_mask_bitcnt = 4,	.ofs_con = 0x260,	.ofs_dat = 0x264,	.ofs_pup = 0x268,	},
	[24] = {/* GPJ2 */	.gpcnt = 8,	.con_mask_bitcnt = 4,	.ofs_con = 0x280,	.ofs_dat = 0x284,	.ofs_pup = 0x288,	},
	[25] = {/* GPJ3 */	.gpcnt = 8,	.con_mask_bitcnt = 4,	.ofs_con = 0x2a0,	.ofs_dat = 0x2a4,	.ofs_pup = 0x2a8,	},
	[26] = {/* GPJ4 */	.gpcnt = 5,	.con_mask_bitcnt = 4,	.ofs_con = 0x2c0,	.ofs_dat = 0x2c4,	.ofs_pup = 0x2c8,	},
};

/// s3c6410 레지스터 어프셋과 정보
static s3c_gpio_register_t  s3c6410_gpio_register['q'-'a'+1] = {
	
	['a'-'a'] = {
				.gpcnt           = 8,
				.con_mask_bitcnt = 4,
				.ofs_con         = 0x0,
				.ofs_dat         = 0x4, 
				.ofs_pup         = 0x8,
		    },
	['b'-'a'] = {
				.gpcnt           = 7,
				.con_mask_bitcnt = 4,
				.ofs_con         = 0x20,
				.ofs_dat         = 0x24, 
				.ofs_pup         = 0x28,
		    },
	['c'-'a'] = {
				.gpcnt           = 8,
				.con_mask_bitcnt = 4,
				.ofs_con         = 0x40,
				.ofs_dat         = 0x44, 
				.ofs_pup         = 0x48,
		    },
	['d'-'a'] = {
				.gpcnt           = 5,
				.con_mask_bitcnt = 4,
				.ofs_con         = 0x60,
				.ofs_dat         = 0x64, 
				.ofs_pup         = 0x68,
		    },
	['e'-'a'] = {
				.gpcnt           = 5,
				.con_mask_bitcnt = 4,
				.ofs_con         = 0x80,
				.ofs_dat         = 0x84, 
				.ofs_pup         = 0x88,
		    },
	['f'-'a'] = {
				.gpcnt           = 16,
				.con_mask_bitcnt = 2,
				.ofs_con         = 0xa0,
				.ofs_dat         = 0xa4, 
				.ofs_pup         = 0xa8,
		    },
	
	['g'-'a'] = {
				.gpcnt           = 7,
				.con_mask_bitcnt = 4,
				.ofs_con         = 0xc0,
				.ofs_dat         = 0xc4, 
				.ofs_pup         = 0xc8,
		    },
	['h'-'a'] = {
				.gpcnt           = 10,
				.con_mask_bitcnt = 4,
				.ofs_con         = 0xe0,
				.ofs_dat         = 0xe8, 
				.ofs_pup         = 0xec,
		    },
	['i'-'a'] = {
				.gpcnt           = 16,
				.con_mask_bitcnt = 2,
				.ofs_con         = 0x100,
				.ofs_dat         = 0x104, 
				.ofs_pup         = 0x108,
		    },
	['j'-'a'] = {
				.gpcnt           = 12,
				.con_mask_bitcnt = 2,
				.ofs_con         = 0x120,
				.ofs_dat         = 0x124, 
				.ofs_pup         = 0x128,
		    },
	['k'-'a'] = {
				.gpcnt           = 16,
				.con_mask_bitcnt = 4,
				.ofs_con         = 0x800,
				.ofs_dat         = 0x808, 
				.ofs_pup         = 0x80c,
		    },
	['l'-'a'] = {
				.gpcnt           = 15,
				.con_mask_bitcnt = 4,
				.ofs_con         = 0x810,
				.ofs_dat         = 0x818, 
				.ofs_pup         = 0x81c,
		    },
	['m'-'a'] = {
				.gpcnt           = 6,
				.con_mask_bitcnt = 4,
				.ofs_con         = 0x820,
				.ofs_dat         = 0x824, 
				.ofs_pup         = 0x828,
		    },
	['n'-'a'] = {
				.gpcnt           = 16,
				.con_mask_bitcnt = 2,
				.ofs_con         = 0x830,
				.ofs_dat         = 0x834, 
				.ofs_pup         = 0x838,
		    },
	['o'-'a'] = {
				.gpcnt           = 16,
				.con_mask_bitcnt = 2,
				.ofs_con         = 0x140,
				.ofs_dat         = 0x144, 
				.ofs_pup         = 0x148,
		    },
	['p'-'a'] = {
				.gpcnt           = 15,
				.con_mask_bitcnt = 2,
				.ofs_con         = 0x160,
				.ofs_dat         = 0x164, 
				.ofs_pup         = 0x168,
		    },
	['q'-'a'] = {
				.gpcnt           = 9,
				.con_mask_bitcnt = 2,
				.ofs_con         = 0x180,
				.ofs_dat         = 0x184, 
				.ofs_pup         = 0x188,
		    }
};
 
/// s3c2440 레지스터 어프셋과 정보 
static s3c_gpio_register_t  s3c2440_gpio_register['j'-'a'+1] = {
	
	['a'-'a'] = {
				.gpcnt           = 16,
				.con_mask_bitcnt = 2,
				.ofs_con         = 0x0,
				.ofs_dat         = 0x4, 
				.ofs_pup         = 0x8,
		    },
	['b'-'a'] = {
				.gpcnt           = 16,
				.con_mask_bitcnt = 2,
				.ofs_con         = 0x10,
				.ofs_dat         = 0x14, 
				.ofs_pup         = 0x18,
		    },
	['c'-'a'] = {
				.gpcnt           = 16,
				.con_mask_bitcnt = 2,
				.ofs_con         = 0x20,
				.ofs_dat         = 0x24, 
				.ofs_pup         = 0x28,
		    },
	['d'-'a'] = {
				.gpcnt           = 16,
				.con_mask_bitcnt = 2,
				.ofs_con         = 0x30,
				.ofs_dat         = 0x34, 
				.ofs_pup         = 0x38,
		    },
	['e'-'a'] = {
				.gpcnt           = 16,
				.con_mask_bitcnt = 2,
				.ofs_con         = 0x40,
				.ofs_dat         = 0x44, 
				.ofs_pup         = 0x48,
		    },
	['f'-'a'] = {
				.gpcnt           = 16,
				.con_mask_bitcnt = 2,
				.ofs_con         = 0x50,
				.ofs_dat         = 0x54, 
				.ofs_pup         = 0x58,
		    },
	['g'-'a'] = {
				.gpcnt           = 16,
				.con_mask_bitcnt = 2,
				.ofs_con         = 0x60,
				.ofs_dat         = 0x64, 
				.ofs_pup         = 0x68,
		    },
	['h'-'a'] = {
				.gpcnt           = 16,
				.con_mask_bitcnt = 2,
				.ofs_con         = 0x70,
				.ofs_dat         = 0x74, 
				.ofs_pup         = 0x78,
		    },
	['i'-'a'] = {
				.gpcnt           = 0,
				.con_mask_bitcnt = 2,
				.ofs_con         = 0,
				.ofs_dat         = 0, 
				.ofs_pup         = 0,
		    },
	['j'-'a'] = {
				.gpcnt           = 13,
				.con_mask_bitcnt = 2,
				.ofs_con         = 0xd0,
				.ofs_dat         = 0xd4, 
				.ofs_pup         = 0xd8,
		    }
};

/// pxa 관련 정보
static pxa_gpio_info_t pxa270_info = {
	.gpcnt = 128,
}; 

static pxa_gpio_info_t pxa255_info = {
	.gpcnt = 96,
}; 

static mcu_gpio_fops_t  mcu_gpio;
//static mmap_alloc_t     map_gpio;       /// mmap 관리 구조체 (0.7.0 부터 삭제)

//------------------------------------------------------------------------------
/** @brief   gpio fops 포인터 얻기
    @return  gpio fops 포인터
*///----------------------------------------------------------------------------
mcu_gpio_fops_t *get_gpio_fops( void )
{
	return &mcu_gpio;
}

//------------------------------------------------------------------------------
/** @brief   gpio 관련된 레지스터를 mmap 을 통해 할다받는다.
    @param   phys_base  물리주소
    @param   phys_size  물리주소 크기
    @return  할당받은 가상 메모리 주소
*///----------------------------------------------------------------------------
static unsigned long malloc_gpio( unsigned long phys_base, unsigned long phys_size )
{
	return (unsigned long)fa_mmap_alloc( &mcu_gpio.map_info, phys_base, phys_size );
}
//------------------------------------------------------------------------------
/** @brief   기본적인 gpio close() 함수

*///----------------------------------------------------------------------------
static void  def_gpio_close( void )
{
	if ( mcu_gpio.mmap_base )
	{
		fa_mmap_free( &mcu_gpio.map_info );
		mcu_gpio.mmap_base = 0;	
	}
}

//------------------------------------------------------------------------------
/** @brief   am3x gpio 번호가 정확한지 확인한다.
    @param   grp      0~3
    @param   gp_nr    gp_nr   번호
*///----------------------------------------------------------------------------
static int am3x_valid_gpio( char grp, int gp_nr )
{
	grp -= '0';
	if ( grp >= MCU_AM3874_GPIO_GRP_CNT ) 
	{
		printf( " gpio error : invalid gp_grp [%d (0~%d)]\n", grp, MCU_AM3874_GPIO_GRP_CNT-1 );
		return -1;
	}

	if ( gp_nr >= 32 ) 
	{
		printf( " gpio error : invalid gp_nr [%d (0~31)]\n", gp_nr );
		return -1;
	}
	
	return 0;
}
//------------------------------------------------------------------------------
/** @brief   am3x gpio 입력 포트 주소를 얻는다.
    @param   grp      0~3
    @param   gp_nr    gp_nr   번호
*///----------------------------------------------------------------------------
static unsigned long am3x_get_input_port ( char grp, int gp_nr )
{
	am3x_gpio_register_t  *gp = (am3x_gpio_register_t *)(mcu_gpio.private) + (grp - '0');
	
	if ( 0 > am3x_valid_gpio( grp, gp_nr ) ) return 0;

	return gp->mmap_base + MCU_AM3874_GPIO_DATAIN;
}
//------------------------------------------------------------------------------
/** @brief   am3x gpio 출력 포트 주소를 얻는다.
    @param   grp      0~3
    @param   gp_nr    gp_nr   번호
*///----------------------------------------------------------------------------
static unsigned long am3x_get_output_port( char grp, int gp_nr )
{
	am3x_gpio_register_t  *gp = (am3x_gpio_register_t *)(mcu_gpio.private) + (grp - '0');
	
	if ( 0 > am3x_valid_gpio( grp, gp_nr ) ) return 0;

	return gp->mmap_base + MCU_AM3874_GPIO_DATAOUT;
}
//------------------------------------------------------------------------------
/** @brief   am3x gpio 입력 설정
    @param   grp      0~3
    @param   gp_nr    gp_nr   번호
    @param   pull_up  pull_up 
*///----------------------------------------------------------------------------
static void  am3x_gpio_dir_input( char grp, int gp_nr, gpio_pulled_t pull_up  )
{
	am3x_gpio_register_t  *gp = (am3x_gpio_register_t *)(mcu_gpio.private) + (grp - '0');
	unsigned long  pin, rval;
	
	if ( 0 > am3x_valid_gpio( grp, gp_nr ) ) return;
	pin = gp->pin_ctrl[gp_nr];

	if ( 0 == pin )
	{
		printf( " gpio error : unable used for gpio [GP%c.%d]\n", grp, gp_nr );
		return;
	}

	// pin ctrl
	rval = MCU_AM3874_PIN_MUX_GPIO | MCU_AM3874_PIN_MUX_IEN;
	switch( pull_up )
	{
	case GPIO_PULL_UP   : rval |= MCU_AM3874_PIN_MUX_IPD;   break;
	case GPIO_PULL_DN   : rval |= MCU_AM3874_PIN_MUX_IPU;   break;
	default             : rval |= MCU_AM3874_PIN_MUX_IPDIS; break;
	}
	AM3874_PIN_CTRL(mcu_gpio.mmap_base, pin) = rval;
	
	// gpio ctrl
	AM3874_GPIO_CTRL( gp->mmap_base ) |= (0x1);	 // enable
	// gpio oe
	AM3874_GPIO_OE( gp->mmap_base ) |= (1<<gp_nr); // dir input
}
//------------------------------------------------------------------------------
/** @brief   am3x gpio 출력 설정
    @param   grp     0~3
    @param   gp_nr   gpio 번호
    @param   gp_val  gpio 초기 출력값
*///----------------------------------------------------------------------------
static void  am3x_gpio_dir_output( char grp, int gp_nr, int gp_val  )
{
	am3x_gpio_register_t  *gp = (am3x_gpio_register_t *)(mcu_gpio.private) + (grp - '0');
	unsigned long  pin, rval;
	
	if ( 0 > am3x_valid_gpio( grp, gp_nr ) ) return;
	pin = gp->pin_ctrl[gp_nr];

	if ( 0 == pin )
	{
		printf( " gpio error : unable used for gpio [GP%c.%d]\n", grp, gp_nr );
		return;
	}

	//printf( "grp-%c  phys=%lx  pin=%d\n", grp, gp->phys, pin );

	// gpio ctrl
	AM3874_GPIO_CTRL( gp->mmap_base ) |= (0x1);	 // enable
	// gpio data out
	{
		rval = AM3874_GPIO_DATAOUT( gp->mmap_base );   
		rval &= ~(1<<gp_nr);
		rval |=  (gp_val&0x1) << gp_nr;
		AM3874_GPIO_DATAOUT( gp->mmap_base ) = rval;
	}
	
	// gpio oe
	AM3874_GPIO_OE( gp->mmap_base ) &= ~(1<<gp_nr); // dir output

	// pin ctrl
	rval = MCU_AM3874_PIN_MUX_GPIO | MCU_AM3874_PIN_MUX_IEN | MCU_AM3874_PIN_MUX_IPDIS;
	AM3874_PIN_CTRL(mcu_gpio.mmap_base,pin) = rval;    
}
//------------------------------------------------------------------------------
/** @brief   am3x gpio 입력
    @param   grp     0~3
    @param   gp_nr   gpio 번호
*///----------------------------------------------------------------------------
static int  am3x_gpio_input( char grp, int gp_nr  )
{
	am3x_gpio_register_t  *gp = (am3x_gpio_register_t *)(mcu_gpio.private) + (grp - '0');
	unsigned long  rval;
	
	if ( 0 > am3x_valid_gpio( grp, gp_nr ) ) return -1;
		
	rval = AM3874_GPIO_DATAIN( gp->mmap_base );

	return ( rval & (1<<gp_nr) ) ? 1:0;
}
//------------------------------------------------------------------------------
/** @brief   pxa gpio 출력
    @param   grp       사용하지 않는다.
    @param   gp_nr     gpio 번호
    @param   gpio_val  gpio 초기 출력값
    
*///----------------------------------------------------------------------------
static void  am3x_gpio_output( char grp, int gp_nr, int gp_val  )
{
	am3x_gpio_register_t  *gp = (am3x_gpio_register_t *)(mcu_gpio.private) + (grp - '0');
	unsigned long  rval;
	
	if ( 0 > am3x_valid_gpio( grp, gp_nr ) ) return;
	
	// gpio data out
	{
		rval = AM3874_GPIO_DATAOUT( gp->mmap_base );   
		rval &= ~(1<<gp_nr);
		rval |=  (gp_val&0x1) << gp_nr;
		AM3874_GPIO_DATAOUT( gp->mmap_base ) = rval;
	}
}
//------------------------------------------------------------------------------
/** @brief   기본적인 gpio close() 함수

*///----------------------------------------------------------------------------
static void  am3x_gpio_close( void )
{
	int lp;
	am3x_gpio_register_t  *gp = (am3x_gpio_register_t *)mcu_gpio.private;
	
	for( lp=0; lp<MCU_AM3874_GPIO_GRP_CNT; lp++, gp++ )
	{
		fa_mmap_free( &gp->map_gpio ); 
	}

	// pin mux
	if ( mcu_gpio.mmap_base )
	{
		fa_mmap_free( &mcu_gpio.map_info );
		mcu_gpio.mmap_base = 0;	
	}
}
//------------------------------------------------------------------------------
/** @brief   am3x gpio  초기화
    @param   mcu_nr  mcu 번호
*///----------------------------------------------------------------------------
static void  am3x_gpio_open( int  mcu_nr )
{
	mcu_gpio.mcu_nr     = mcu_nr;
	// pin mux mmap
	mcu_gpio.mmap_base  = malloc_gpio( MCU_AM3874_PIN_MUX_PHYS, MCU_AM3874_PIN_MUX_SIZE );

	// gpio mmap
	{
		int lp;
		am3x_gpio_register_t  *gp = am3874_gpio_register;

		for( lp=0; lp<MCU_AM3874_GPIO_GRP_CNT; lp++, gp++ )
		{
			gp->mmap_base = (unsigned long)fa_mmap_alloc( &gp->map_gpio, gp->phys, MCU_AM3874_MMAP_GPIO_SIZE ); 
		}
	}
		
	mcu_gpio.close           = am3x_gpio_close     ;
	mcu_gpio.dir_input       = am3x_gpio_dir_input ;
	mcu_gpio.dir_output      = am3x_gpio_dir_output;
	mcu_gpio.input           = am3x_gpio_input     ;
	mcu_gpio.output          = am3x_gpio_output    ;
	mcu_gpio.get_input_port  = am3x_get_input_port ;
	mcu_gpio.get_output_port = am3x_get_output_port;
	
	mcu_gpio.private    = (void *)am3874_gpio_register;
}

//------------------------------------------------------------------------------
/** @brief   s3c gpio 번호를 검사하여 해당하는 포이터를 돌려준다.
	
*///----------------------------------------------------------------------------
static s3c_gpio_register_t *s3c_find_reg( char grp, int gp_nr )
{
	s3c_gpio_register_t *reg;
	unsigned long offset;
	
	offset = 0;
	
	if ( ( grp < mcu_gpio.grp_begin ) || ( grp > mcu_gpio.grp_end )  )
	{
		printf( " gpio error : invalid group [%c]\n", grp );
		return NULL; 	
	}

	switch( mcu_gpio.mcu_nr )
	{
	case 210 :
		{
			offset = (grp - 'a') + (gp_nr / GP_NR_GRP)*(mcu_gpio.grp_end - mcu_gpio.grp_begin + 1);
			if ( offset >= sizeof(s5pv210_reg_idx_table)/sizeof(int) ) 
			{
				printf( " gpio error : invalid gp_grp ro gp_nr [%c%d-%d]\n", grp, gp_nr/GP_NR_GRP, gp_nr%GP_NR_GRP );
				return NULL;
			}
				
			offset = s5pv210_reg_idx_table[ offset ];
			//if ( offset < 0 )
			//{
			//	printf( " gpio error : invalid gp_grp ro gp_nr [%c%d-%d]\n", grp, gp_nr/GP_NR_GRP, gp_nr%GP_NR_GRP );
			//	return NULL;
			//}
			
			//printf( " offset=%d\n", offset );
			gp_nr %= GP_NR_GRP;
		}
		break;
	
	default  :
		offset = (grp - 'a');
		break;
	}

	reg = (s3c_gpio_register_t *)(mcu_gpio.private) + offset;

	if ( ( 0 > gp_nr ) || ( reg->gpcnt <= gp_nr ) )
	{
		printf( " gpio error : invalid gp_nr [%d (0~%d)]\n", gp_nr, reg->gpcnt-1 );
		return NULL; 	
	}
	
	return reg;
}
//------------------------------------------------------------------------------
/** @brief   s3c gpio 입력 설정
    @param   grp      gpio    그룹문자 'a' ... 'z'
    @param   gp_nr    gp_nr   번호
    @param   pull_up  pull_up 내부 풀업 활성화 유무
*///----------------------------------------------------------------------------
static void  s3c_gpio_dir_input( char grp, int gp_nr, gpio_pulled_t pull_up  )
{
	volatile unsigned long *reg_con;
	volatile unsigned long *reg_pup;
	volatile unsigned long  rval;
	s3c_gpio_register_t    *reg;

	reg = s3c_find_reg( grp, gp_nr );
	gp_nr %= GP_NR_GRP;
	if ( NULL == reg ) return;

	reg_con = (unsigned long *)(mcu_gpio.mmap_base + reg->ofs_con );
	reg_pup = (unsigned long *)(mcu_gpio.mmap_base + reg->ofs_pup );

	// pull-up 설정
	{
		rval  = *(reg_pup);
		rval &= ~(0x3<<(gp_nr*2));  
		
		switch( pull_up )
		{
		case GPIO_PULL_UP   : rval |= (0x2<<(gp_nr*2));  break;
		case GPIO_PULL_DN   : rval |= (0x1<<(gp_nr*2));  break;
		case GPIO_PULL_NONE : break;
		}
		
		*(reg_pup) = rval;
    }
	
	// 입력 설정
	{
		unsigned long  and_mask, orr_mask;
		
		if ( reg->con_mask_bitcnt == 4 )
		{
			reg_con +=  (gp_nr/8);
			and_mask = ~( 0xf << ( (gp_nr%8 )*4 ) ); 	
			orr_mask = 	( 0x0 << ( (gp_nr%8 )*4 ) );	
		}
		else 
		{
			and_mask = ~( 0x3 << ( (gp_nr%16)*2 ) ); 			
			orr_mask = 	( 0x0 << ( (gp_nr%16)*2 ) );	
		}
		
		rval = *(reg_con);
		rval &= and_mask;
		rval |= orr_mask;
		*(reg_con) = rval;
	}
}
//------------------------------------------------------------------------------
/** @brief   s3c gpio 출력 설정
    @param   grp     gpio 그룹
    @param   gp_nr   gpio 번호
    @param   gp_val  gpio 초기 출력값
*///----------------------------------------------------------------------------
static void  s3c_gpio_dir_output( char grp, int gp_nr, int gp_val  )
{
	volatile unsigned long *reg_con;
	volatile unsigned long *reg_dat;
	volatile unsigned long *reg_pup;
	volatile unsigned long  rval;
	s3c_gpio_register_t    *reg;

	reg = s3c_find_reg( grp, gp_nr );
	gp_nr %= GP_NR_GRP;
	if ( NULL == reg ) return;

	reg_con = (unsigned long *)(mcu_gpio.mmap_base + reg->ofs_con );
	reg_dat = (unsigned long *)(mcu_gpio.mmap_base + reg->ofs_dat );
	reg_pup = (unsigned long *)(mcu_gpio.mmap_base + reg->ofs_pup );

	// pull-up 설정
	{
		rval  = *(reg_pup);
		rval &= ~(0x3<<(gp_nr*2));  // pull-up disable
		*(reg_pup) = rval;
    }

	// 출력
	{
		rval = *(reg_dat);
		rval &= ~(1<<gp_nr);
		rval |=  (gp_val&0x1) << gp_nr;
		*(reg_dat) = rval;
	}

	// 출력 설정
	{
		unsigned long  and_mask, orr_mask;
		
		if ( reg->con_mask_bitcnt == 4 )
		{
			reg_con +=  (gp_nr/8);
			and_mask = ~( 0xf << ( (gp_nr%8 )*4 ) ); 	
			orr_mask = 	( 0x1 << ( (gp_nr%8 )*4 ) );	
		}
		else 
		{
			and_mask = ~( 0x3 << ( (gp_nr%16)*2 ) ); 			
			orr_mask = 	( 0x1 << ( (gp_nr%16)*2 ) );	
		}

		rval = *(reg_con);
		rval &= and_mask;
		rval |= orr_mask;
		*(reg_con) = rval;
	}
}
//------------------------------------------------------------------------------
/** @brief   s3c gpio 입력
    @param   grp     gpio 그룹
    @param   gp_nr   gpio 번호
*///----------------------------------------------------------------------------
static int  s3c_gpio_input( char grp, int gp_nr  )
{
	volatile unsigned long *reg_dat;
	s3c_gpio_register_t    *reg;

	reg = s3c_find_reg( grp, gp_nr );
	gp_nr %= GP_NR_GRP;
	if ( NULL == reg ) return -1;

	reg_dat = (unsigned long *)(mcu_gpio.mmap_base + reg->ofs_dat );

	return *(reg_dat) & (1<< gp_nr) ? 1 : 0;
}
//------------------------------------------------------------------------------
/** @brief   s3c gpio 출력
    @param   grp  gpio 그룹
    @param   gp_nr   gpio 번호
    @param   gpio_val  gpio 초기 출력값
    
*///----------------------------------------------------------------------------
static void  s3c_gpio_output( char grp, int gp_nr, int gp_val  )
{
	volatile unsigned long *reg_dat;
	volatile unsigned long  rval;
	s3c_gpio_register_t    *reg;

	reg = s3c_find_reg( grp, gp_nr );
	gp_nr %= GP_NR_GRP;
	if ( NULL == reg ) return;

	reg_dat = (unsigned long *)(mcu_gpio.mmap_base + reg->ofs_dat );

	// 출력
	rval = *(reg_dat);
	rval &= ~(1<<gp_nr);
	rval |=  (gp_val&0x1) << gp_nr;
	*(reg_dat) = rval;
}
//------------------------------------------------------------------------------
/** @brief   s3c, s5p gpio 출력 포트 주소를 얻는다.
    @param   grp      0~3
    @param   gp_nr    gp_nr   번호
*///----------------------------------------------------------------------------
static unsigned long s3c_get_data_port( char grp, int gp_nr )
{
	s3c_gpio_register_t *reg;
	
	reg = s3c_find_reg( grp, gp_nr );
	gp_nr %= GP_NR_GRP;
	if ( NULL == reg ) return 0;

	return (unsigned long )(mcu_gpio.mmap_base + reg->ofs_dat );
}

//------------------------------------------------------------------------------
/** @brief   s3c gpio  초기화
    @param   mcu_nr  mcu 번호
*///----------------------------------------------------------------------------
static void  s3c_gpio_open( int  mcu_nr )
{
	mcu_gpio.mcu_nr = mcu_nr;
	
	switch( mcu_nr )
	{
	case 210 :	// S5PV210
		{
			mcu_gpio.grp_begin  = 'a';
			mcu_gpio.grp_end    = 'j';
			mcu_gpio.phys_base  = MCU_S5PV210_MMAP_GPIO_PHYS;
			mcu_gpio.phys_size  = MCU_S5PV210_MMAP_GPIO_SIZE;
			mcu_gpio.private    = (void *)s5pv210_gpio_register;
			mcu_gpio.get_output_port = s3c_get_data_port;
			mcu_gpio.get_input_port  = s3c_get_data_port;
		}
		break;

	case 6410 :
		{
			mcu_gpio.grp_begin  = 'a';
			mcu_gpio.grp_end    = 'a' + sizeof(s3c6410_gpio_register)/sizeof(s3c_gpio_register_t);
			mcu_gpio.phys_base  = MCU_S3C6410_MMAP_GPIO_PHYS;
			mcu_gpio.phys_size  = MCU_S3C6410_MMAP_GPIO_SIZE;
			mcu_gpio.private    = (void *)s3c6410_gpio_register;
			mcu_gpio.get_output_port = s3c_get_data_port;
			mcu_gpio.get_input_port  = s3c_get_data_port;
		}
		break;

	default :		
		{
			mcu_gpio.grp_begin  = 'a';
			mcu_gpio.grp_end    = 'a' + sizeof(s3c2440_gpio_register)/sizeof(s3c_gpio_register_t);
			mcu_gpio.phys_base  = MCU_S3C2440_MMAP_GPIO_PHYS;
			mcu_gpio.phys_size  = MCU_S3C2440_MMAP_GPIO_SIZE;
			mcu_gpio.private    = (void *)s3c2440_gpio_register;
		}
		break;
	}

	mcu_gpio.mmap_base  = malloc_gpio( mcu_gpio.phys_base, mcu_gpio.phys_size );
	
	mcu_gpio.close      = def_gpio_close     ;
	mcu_gpio.dir_input  = s3c_gpio_dir_input ;
	mcu_gpio.dir_output = s3c_gpio_dir_output;
	mcu_gpio.input      = s3c_gpio_input     ;
	mcu_gpio.output     = s3c_gpio_output    ;
}

//------------------------------------------------------------------------------
/** @brief   pxa gpio 입력 설정
    @param   grp      gpio    사용하지 않는다.
    @param   gp_nr    gp_nr   번호
    @param   pull_up  pull_up 사용하지 않는다.
*///----------------------------------------------------------------------------
static void  pxa_gpio_dir_input( char grp, int gp_nr, gpio_pulled_t pull_up  )
{
	pxa_gpio_info_t  *info;
	
	grp     = grp;
	pull_up = pull_up;

	info = (pxa_gpio_info_t *)(mcu_gpio.private);
	if ( info->gpcnt <= gp_nr ) 
	{
		printf( " gpio error : invalid gp_nr [%d (0~%d)]\n", gp_nr, info->gpcnt-1 );
		return;
	}
	
	// gpio in/out 설정
	PXA_GPIO_INOUT( gp_nr );

	// 입력 설정
	PXA_DIR_IN( gp_nr );
}
//------------------------------------------------------------------------------
/** @brief   pxa gpio 출력 설정
    @param   grp     사용하지 않는다.
    @param   gp_nr   gpio 번호
    @param   gp_val  gpio 초기 출력값
*///----------------------------------------------------------------------------
static void  pxa_gpio_dir_output( char grp, int gp_nr, int gp_val  )
{
	pxa_gpio_info_t  *info;

	grp  = grp;

	info = (pxa_gpio_info_t *)(mcu_gpio.private);
	if ( info->gpcnt <= gp_nr ) 
	{
		printf( " gpio error : invalid gp_nr [%d (0~%d)]\n", gp_nr, info->gpcnt-1 );
		return;
	}

	// gpio in/out 설정
	PXA_GPIO_INOUT( gp_nr );
	
	// 출력
	if (gp_val & 0x1)
	{
		PXA_OUT_SET(gp_nr);
	}
	else 
	{
		PXA_OUT_CLR(gp_nr);
	}

	// 출력 설정
	PXA_DIR_OUT( gp_nr );
}
//------------------------------------------------------------------------------
/** @brief   pxa gpio 입력
    @param   grp     사용하지 않는다.
    @param   gp_nr   gpio 번호
*///----------------------------------------------------------------------------
static int  pxa_gpio_input( char grp, int gp_nr  )
{
	pxa_gpio_info_t  *info;

	grp  = grp;

	info = (pxa_gpio_info_t *)(mcu_gpio.private);
	if ( info->gpcnt <= gp_nr ) 
	{
		printf( " gpio error : invalid gp_nr [%d (0~%d)]\n", gp_nr, info->gpcnt-1 );
		return -1;
	}

	return PXA_IN_DAT( gp_nr ) ? 1:0;
}
//------------------------------------------------------------------------------
/** @brief   pxa gpio 출력
    @param   grp       사용하지 않는다.
    @param   gp_nr     gpio 번호
    @param   gpio_val  gpio 초기 출력값
    
*///----------------------------------------------------------------------------
static void  pxa_gpio_output( char grp, int gp_nr, int gp_val  )
{
	pxa_gpio_info_t  *info;

	grp  = grp;

	info = (pxa_gpio_info_t *)(mcu_gpio.private);
	if ( info->gpcnt <= gp_nr ) 
	{
		printf( " gpio error : invalid gp_nr [%d (0~%d)]\n", gp_nr, info->gpcnt-1 );
		return;
	}

	if (gp_val & 0x1)
	{
		PXA_OUT_SET(gp_nr);
	}
	else 
	{
		PXA_OUT_CLR(gp_nr);
	}
}
//------------------------------------------------------------------------------
/** @brief   pxa gpio  초기화
    @param   mcu_nr  mcu 번호
*///----------------------------------------------------------------------------
static void  pxa_gpio_open( int  mcu_nr )
{
	mcu_gpio.mcu_nr     = mcu_nr;
	
	if ( 270 == mcu_nr )
	{
		mcu_gpio.phys_base  = MCU_PXA270_MMAP_GPIO_PHYS;
		mcu_gpio.phys_size  = MCU_PXA270_MMAP_GPIO_SIZE;
		mcu_gpio.private    = (void *)&pxa270_info;
	}
	else 
	{
		mcu_gpio.phys_base  = MCU_PXA255_MMAP_GPIO_PHYS;
		mcu_gpio.phys_size  = MCU_PXA255_MMAP_GPIO_SIZE;
		mcu_gpio.private    = (void *)&pxa255_info;
	}

	mcu_gpio.mmap_base  = malloc_gpio( mcu_gpio.phys_base, mcu_gpio.phys_size );
	
	mcu_gpio.close      = def_gpio_close     ;
	mcu_gpio.dir_input  = pxa_gpio_dir_input ;
	mcu_gpio.dir_output = pxa_gpio_dir_output;
	mcu_gpio.input      = pxa_gpio_input     ;
	mcu_gpio.output     = pxa_gpio_output    ;
}

//------------------------------------------------------------------------------
/** @brief   ixp gpio 입력 설정
    @param   grp      gpio    사용하지 않는다.
    @param   gp_nr    gp_nr   번호
    @param   pull_up  pull_up 사용하지 않는다.
*///----------------------------------------------------------------------------
static void  ixp_gpio_dir_input( char grp, int gp_nr, gpio_pulled_t pull_up  )
{
	grp     = grp;
	pull_up = pull_up;

	
	if ( ( 0 <= gp_nr ) && ( gp_nr <= 15 ) )
	{
		IXP4XX_GPIO_GPOER |=  (1<<gp_nr);	
	}
	else
	{
		printf( " gpio error : invalid gp_nr [%d (0~15)]\n", gp_nr );
	}
}
//------------------------------------------------------------------------------
/** @brief   ixp gpio 출력 설정
    @param   grp     사용하지 않는다.
    @param   gp_nr   gpio 번호
    @param   gp_val  gpio 초기 출력값
*///----------------------------------------------------------------------------
static void  ixp_gpio_dir_output( char grp, int gp_nr, int gp_val  )
{
	grp     = grp;
	
	if ( ( 0 <= gp_nr ) && ( gp_nr <= 15 ) )
	{
		if ( gp_val )
		{
			IXP4XX_GPIO_GPOUTR |=  (1<<gp_nr);	
		}
		else
		{
			IXP4XX_GPIO_GPOUTR &= ~(1<<gp_nr);	
		}
		
		IXP4XX_GPIO_GPOER  &= ~(1<<gp_nr);	
	}
	else
	{
		printf( " gpio error : invalid gp_nr [%d (0~15)]\n", gp_nr );
	}
}
//------------------------------------------------------------------------------
/** @brief   ixp gpio 입력
    @param   grp     사용하지 않는다.
    @param   gp_nr   gpio 번호
*///----------------------------------------------------------------------------
static int  ixp_gpio_input( char grp, int gp_nr  )
{
	unsigned int val;
	grp     = grp;
	
	if ( ( 0 <= gp_nr ) && ( gp_nr <= 15 ) )
	{
		val = IXP4XX_GPIO_GPINR;
		return (val & (1<<gp_nr)) ? 1:0;
	}
	
	return 0;
}
//------------------------------------------------------------------------------
/** @brief   ixp gpio 출력
    @param   grp       사용하지 않는다.
    @param   gp_nr     gpio 번호
    @param   gpio_val  gpio 초기 출력값
    
*///----------------------------------------------------------------------------
static void  ixp_gpio_output( char grp, int gp_nr, int gp_val  )
{
	grp     = grp;

	if ( ( 0 <= gp_nr ) && ( gp_nr <= 15 ) )
	{
		if ( gp_val )
		{
			IXP4XX_GPIO_GPOUTR |=  (1<<gp_nr);	
		}
		else
		{
			IXP4XX_GPIO_GPOUTR &= ~(1<<gp_nr);	
		}
	}
}
//------------------------------------------------------------------------------
/** @brief   ixp gpio  초기화
    @param   mcu_nr  mcu 번호
*///----------------------------------------------------------------------------
static void  ixp_gpio_open( int  mcu_nr )
{
	mcu_gpio.mcu_nr     = mcu_nr;
	
	mcu_gpio.phys_base  = MCU_IXP4XX_MMAP_GPIO_PHYS;
	mcu_gpio.phys_size  = MCU_IXP4XX_MMAP_GPIO_SIZE;
	mcu_gpio.private    = NULL; 


	mcu_gpio.mmap_base  = malloc_gpio( mcu_gpio.phys_base, mcu_gpio.phys_size );
	
	mcu_gpio.close      = def_gpio_close     ;
	mcu_gpio.dir_input  = ixp_gpio_dir_input ;
	mcu_gpio.dir_output = ixp_gpio_dir_output;
	mcu_gpio.input      = ixp_gpio_input     ;
	mcu_gpio.output     = ixp_gpio_output    ;
}






//=============================================================================//
//=============================================================================//


//------------------------------------------------------------------------------
/** @brief   mcu 를 선택한 후 mmap 메모리를 맵핑한다. 
    @param   mcu  사용할 MCU 번호를 선택한다.
    @return  성공 0, 에러 -1
*///----------------------------------------------------------------------------
int tgpio_open( int mcu )
{
	if ( mcu_gpio.mmap_base ) tgpio_close();

	switch( mcu )
	{
	case MCU_iMX6    :  imx6_gpio_open(mcu); break;
	case MCU_AM3874  :  am3x_gpio_open(mcu); break;
	case MCU_S5PV210 :  s3c_gpio_open(mcu) ; break; 		
	case MCU_S3C6410 :  s3c_gpio_open(mcu) ; break; 		
	case MCU_S3C2440 :  s3c_gpio_open(mcu) ; break; 		
	case MCU_PXA270  :  pxa_gpio_open(mcu) ; break; 		
	case MCU_PXA255  :  pxa_gpio_open(mcu) ; break; 	
	case MCU_IXP420  :
	case MCU_IXP430  :
	case MCU_IXP435  :  ixp_gpio_open(mcu) ; break;
		
	default :
		printf( " gpio error : unknown mcu_nr [%d]\n", mcu );
		return -1;	
	}

    return 0;
}
//------------------------------------------------------------------------------
/** @brief   gpio 맵핑을 해제한다..
    @remark  
*///----------------------------------------------------------------------------
void tgpio_close( void )
{
	if ( mcu_gpio.close ) mcu_gpio.close();
	memset( &mcu_gpio, 0, sizeof(mcu_gpio_fops_t) );
}
//------------------------------------------------------------------------------
/** @brief   gpio 를 입력으로 설정한다.
    @param   grp      gpio    그룹문자 'a' ... 'z'
    @param   gp_nr    gp_nr   번호
    @param   pull_up  pull_up 내부 풀업 활성화 유무
*///----------------------------------------------------------------------------
void  tgpio_dir_input( char grp, int gp_nr, gpio_pulled_t pull_up )
{
	if ( mcu_gpio.dir_input ) mcu_gpio.dir_input( grp, gp_nr, pull_up );
}
//------------------------------------------------------------------------------
/** @brief   gpio 를 출력으로 설정한다.
    @param   grp     그룹문자 'a' ... 'z'
    @param   gp_nr   gpio 번호
    @param   gp_val  gpio 초기 출력값
*///----------------------------------------------------------------------------
void  tgpio_dir_output( char grp, int gp_nr, int gp_val )
{
	if ( mcu_gpio.dir_output ) mcu_gpio.dir_output( grp, gp_nr, gp_val );
}
//------------------------------------------------------------------------------
/** @brief   gpio 의 입력값을 읽는다.
    @param   grp      gpio    그룹문자 'a' ... 'z'
    @param   gp_nr    gp_nr   번호
    @return  성공 0 or 1   에러일 경우 음수 
*///----------------------------------------------------------------------------
int  tgpio_input( char grp, int gp_nr )
{
	if ( mcu_gpio.input ) return mcu_gpio.input( grp, gp_nr );
		
	return -1;
}

//------------------------------------------------------------------------------
/** @brief   gpio 에 값을 쓴다
    @param   grp      gpio    그룹문자 'a' ... 'z'
    @param   gp_nr    gp_nr   번호
    @param   gp_val   gpio    출력값
*///----------------------------------------------------------------------------
void tgpio_output( char grp, int gp_nr, int gp_val )
{
	if ( mcu_gpio.output ) mcu_gpio.output( grp, gp_nr, gp_val );
}
//------------------------------------------------------------------------------
/** @brief   gpio 의 입력 포트 포인터를 얻는다.
    @param   grp      gpio    그룹문자 'a' ... 'z'
    @param   gp_nr    gp_nr   번호
    @return  포트주소
*///----------------------------------------------------------------------------
unsigned long tgpio_get_input_port ( char grp, int gp_nr ) 
{
	if ( mcu_gpio.get_input_port ) return mcu_gpio.get_input_port( grp, gp_nr );
	return 0;
}
//------------------------------------------------------------------------------
/** @brief   gpio 의 출력 포트 포인터를 얻는다.
    @param   grp      gpio    그룹문자 'a' ... 'z'
    @param   gp_nr    gp_nr   번호
    @return  포트주소
*///----------------------------------------------------------------------------
unsigned long tgpio_get_output_port( char grp, int gp_nr )
{
	if ( mcu_gpio.get_output_port ) return mcu_gpio.get_output_port( grp, gp_nr );
	return 0;
}