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

              Ver 0.7.0    iMX6 포함, GP0테스트 진행

              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>
#include <tgpio_imx6q-iomux.h>


extern mcu_gpio_fops_t *get_gpio_fops( void );
static mcu_gpio_fops_t *mgpio = NULL;

/// imx6 gpio base 
static imx6_gpio_register_t imx6_gpio_register[MCU_iMX6_GPIO_GRP_CNT] = { 
	[0] = { .phys = MCU_iMX6_GPIO1_PHYS, },
	[1] = { .phys = MCU_iMX6_GPIO2_PHYS, },
	[2] = { .phys = MCU_iMX6_GPIO3_PHYS, },
	[3] = { .phys = MCU_iMX6_GPIO4_PHYS, },
	[4] = { .phys = MCU_iMX6_GPIO5_PHYS, },
	[5] = { .phys = MCU_iMX6_GPIO6_PHYS, },
	[6] = { .phys = MCU_iMX6_GPIO7_PHYS, },
};


#define  WRITE_MUX_CTRL(  val, reg )	*(volatile unsigned long *)( reg ) = (val)
#define  WRITE_SEL_INPUT( val, reg )	*(volatile unsigned long *)( reg ) = (val)
#define  WRITE_PAD_CTRL(  val, reg )	*(volatile unsigned long *)( reg ) = (val)

static void  imx6_gpio_output( char grp, int gp_nr, int gp_val  );

//------------------------------------------------------------------------------
/** @brief   configures a single pad in the iomuxer
    @param   mmap_base
    @param   pad
*///----------------------------------------------------------------------------
static int mxc_iomux_v3_setup_pad( unsigned long base, iomux_v3_cfg_t pad )
{
	u32 mux_ctrl_ofs  = (pad & MUX_CTRL_OFS_MASK     ) >> MUX_CTRL_OFS_SHIFT     ;
	u32 mux_mode      = (pad & MUX_MODE_MASK         ) >> MUX_MODE_SHIFT         ;
	u32 sel_input_ofs = (pad & MUX_SEL_INPUT_OFS_MASK) >> MUX_SEL_INPUT_OFS_SHIFT;
	u32 sel_input     = (pad & MUX_SEL_INPUT_MASK    ) >> MUX_SEL_INPUT_SHIFT    ;
	u32 pad_ctrl_ofs  = (pad & MUX_PAD_CTRL_OFS_MASK ) >> MUX_PAD_CTRL_OFS_SHIFT ;
	u32 pad_ctrl      = (pad & MUX_PAD_CTRL_MASK     ) >> MUX_PAD_CTRL_SHIFT     ;

	if (mux_ctrl_ofs)
		WRITE_MUX_CTRL(mux_mode, base + mux_ctrl_ofs);

	if (sel_input_ofs)
		WRITE_SEL_INPUT(sel_input, base + sel_input_ofs);

	if (!(pad_ctrl & NO_PAD_CTRL) && pad_ctrl_ofs)
		WRITE_PAD_CTRL(pad_ctrl, base + pad_ctrl_ofs);

	return 0;
}

// group : gpio1 ... gpio7

static iomux_v3_cfg_t imx6q_gpio_pads[7][32] = {
	['1'-'1'] = {
		MX6Q_PAD_GPIO_0__GPIO_1_0,
		MX6Q_PAD_GPIO_1__GPIO_1_1,
		MX6Q_PAD_GPIO_2__GPIO_1_2,
		MX6Q_PAD_GPIO_3__GPIO_1_3,
		MX6Q_PAD_GPIO_4__GPIO_1_4,
		MX6Q_PAD_GPIO_5__GPIO_1_5,
		MX6Q_PAD_GPIO_6__GPIO_1_6,
		MX6Q_PAD_GPIO_7__GPIO_1_7,
		MX6Q_PAD_GPIO_8__GPIO_1_8,
		MX6Q_PAD_GPIO_9__GPIO_1_9,
		MX6Q_PAD_SD2_CLK__GPIO_1_10,
		MX6Q_PAD_SD2_CMD__GPIO_1_11,
		MX6Q_PAD_SD2_DAT3__GPIO_1_12,
		MX6Q_PAD_SD2_DAT2__GPIO_1_13,
		MX6Q_PAD_SD2_DAT1__GPIO_1_14,
		MX6Q_PAD_SD2_DAT0__GPIO_1_15,
		MX6Q_PAD_SD1_DAT0__GPIO_1_16,
		MX6Q_PAD_SD1_DAT1__GPIO_1_17,
		MX6Q_PAD_SD1_CMD__GPIO_1_18,
		MX6Q_PAD_SD1_DAT2__GPIO_1_19,
		MX6Q_PAD_SD1_CLK__GPIO_1_20,
		MX6Q_PAD_SD1_DAT3__GPIO_1_21,
		MX6Q_PAD_ENET_MDIO__GPIO_1_22,
		MX6Q_PAD_ENET_REF_CLK__GPIO_1_23,
		MX6Q_PAD_ENET_RX_ER__GPIO_1_24,
		MX6Q_PAD_ENET_CRS_DV__GPIO_1_25,
		MX6Q_PAD_ENET_RXD1__GPIO_1_26,
		MX6Q_PAD_ENET_RXD0__GPIO_1_27,
		MX6Q_PAD_ENET_TX_EN__GPIO_1_28,
		MX6Q_PAD_ENET_TXD1__GPIO_1_29,
		MX6Q_PAD_ENET_TXD0__GPIO_1_30,
		MX6Q_PAD_ENET_MDC__GPIO_1_31,
	},
	['2'-'1'] = {
		MX6Q_PAD_NANDF_D0__GPIO_2_0,
		MX6Q_PAD_NANDF_D1__GPIO_2_1,
		MX6Q_PAD_NANDF_D2__GPIO_2_2,
		MX6Q_PAD_NANDF_D3__GPIO_2_3,
		MX6Q_PAD_NANDF_D4__GPIO_2_4,
		MX6Q_PAD_NANDF_D5__GPIO_2_5,
		MX6Q_PAD_NANDF_D6__GPIO_2_6,
		MX6Q_PAD_NANDF_D7__GPIO_2_7,
		MX6Q_PAD_SD4_DAT0__GPIO_2_8,
		MX6Q_PAD_SD4_DAT1__GPIO_2_9,
		MX6Q_PAD_SD4_DAT2__GPIO_2_10,
		MX6Q_PAD_SD4_DAT3__GPIO_2_11,
		MX6Q_PAD_SD4_DAT4__GPIO_2_12,
		MX6Q_PAD_SD4_DAT5__GPIO_2_13,
		MX6Q_PAD_SD4_DAT6__GPIO_2_14,
		MX6Q_PAD_SD4_DAT7__GPIO_2_15,		
		MX6Q_PAD_EIM_A22__GPIO_2_16,
		MX6Q_PAD_EIM_A21__GPIO_2_17,
		MX6Q_PAD_EIM_A20__GPIO_2_18,
		MX6Q_PAD_EIM_A19__GPIO_2_19,
		MX6Q_PAD_EIM_A18__GPIO_2_20,
		MX6Q_PAD_EIM_A17__GPIO_2_21,
		MX6Q_PAD_EIM_A16__GPIO_2_22,
		MX6Q_PAD_EIM_CS0__GPIO_2_23,
		MX6Q_PAD_EIM_CS1__GPIO_2_24,
		MX6Q_PAD_EIM_OE__GPIO_2_25,
		MX6Q_PAD_EIM_RW__GPIO_2_26,
		MX6Q_PAD_EIM_LBA__GPIO_2_27,
		MX6Q_PAD_EIM_EB0__GPIO_2_28,
		MX6Q_PAD_EIM_EB1__GPIO_2_29,
		MX6Q_PAD_EIM_EB2__GPIO_2_30,
		MX6Q_PAD_EIM_EB3__GPIO_2_31,
	},
	['3'-'1'] = {
		MX6Q_PAD_EIM_DA0__GPIO_3_0,
		MX6Q_PAD_EIM_DA1__GPIO_3_1,
		MX6Q_PAD_EIM_DA2__GPIO_3_2,
		MX6Q_PAD_EIM_DA3__GPIO_3_3,
		MX6Q_PAD_EIM_DA4__GPIO_3_4,
		MX6Q_PAD_EIM_DA5__GPIO_3_5,
		MX6Q_PAD_EIM_DA6__GPIO_3_6,
		MX6Q_PAD_EIM_DA7__GPIO_3_7,
		MX6Q_PAD_EIM_DA8__GPIO_3_8,
		MX6Q_PAD_EIM_DA9__GPIO_3_9,
		MX6Q_PAD_EIM_DA10__GPIO_3_10,
		MX6Q_PAD_EIM_DA11__GPIO_3_11,
		MX6Q_PAD_EIM_DA12__GPIO_3_12,
		MX6Q_PAD_EIM_DA13__GPIO_3_13,
		MX6Q_PAD_EIM_DA14__GPIO_3_14,
		MX6Q_PAD_EIM_DA15__GPIO_3_15,		
		MX6Q_PAD_EIM_D16__GPIO_3_16,
		MX6Q_PAD_EIM_D17__GPIO_3_17,
		MX6Q_PAD_EIM_D18__GPIO_3_18,
		MX6Q_PAD_EIM_D19__GPIO_3_19,
		MX6Q_PAD_EIM_D20__GPIO_3_20,
		MX6Q_PAD_EIM_D21__GPIO_3_21,
		MX6Q_PAD_EIM_D22__GPIO_3_22,
		MX6Q_PAD_EIM_D23__GPIO_3_23,
		MX6Q_PAD_EIM_D24__GPIO_3_24,
		MX6Q_PAD_EIM_D25__GPIO_3_25,
		MX6Q_PAD_EIM_D26__GPIO_3_26,
		MX6Q_PAD_EIM_D27__GPIO_3_27,
		MX6Q_PAD_EIM_D28__GPIO_3_28,
		MX6Q_PAD_EIM_D29__GPIO_3_29,
		MX6Q_PAD_EIM_D30__GPIO_3_30,
		MX6Q_PAD_EIM_D31__GPIO_3_31,
	},
	['4'-'1'] = {
		0,0,0,0,0,
		MX6Q_PAD_GPIO_19__GPIO_4_5,
		MX6Q_PAD_KEY_COL0__GPIO_4_6,
		MX6Q_PAD_KEY_ROW0__GPIO_4_7,
		MX6Q_PAD_KEY_COL1__GPIO_4_8,
		MX6Q_PAD_KEY_ROW1__GPIO_4_9,
		MX6Q_PAD_KEY_COL2__GPIO_4_10,
		MX6Q_PAD_KEY_ROW2__GPIO_4_11,
		MX6Q_PAD_KEY_COL3__GPIO_4_12,
		MX6Q_PAD_KEY_ROW3__GPIO_4_13,
		MX6Q_PAD_KEY_COL4__GPIO_4_14,
		MX6Q_PAD_KEY_ROW4__GPIO_4_15,
		MX6Q_PAD_DI0_DISP_CLK__GPIO_4_16,
		MX6Q_PAD_DI0_PIN15__GPIO_4_17,
		MX6Q_PAD_DI0_PIN2__GPIO_4_18,
		MX6Q_PAD_DI0_PIN3__GPIO_4_19,
		MX6Q_PAD_DI0_PIN4__GPIO_4_20,
		MX6Q_PAD_DISP0_DAT0__GPIO_4_21,
		MX6Q_PAD_DISP0_DAT1__GPIO_4_22,
		MX6Q_PAD_DISP0_DAT2__GPIO_4_23,
		MX6Q_PAD_DISP0_DAT3__GPIO_4_24,
		MX6Q_PAD_DISP0_DAT4__GPIO_4_25,
		MX6Q_PAD_DISP0_DAT5__GPIO_4_26,
		MX6Q_PAD_DISP0_DAT6__GPIO_4_27,
		MX6Q_PAD_DISP0_DAT7__GPIO_4_28,
		MX6Q_PAD_DISP0_DAT8__GPIO_4_29,
		MX6Q_PAD_DISP0_DAT9__GPIO_4_30,
		MX6Q_PAD_DISP0_DAT10__GPIO_4_31,
	},
	['5'-'1'] = {
		MX6Q_PAD_EIM_WAIT__GPIO_5_0,
		0,
		MX6Q_PAD_EIM_A25__GPIO_5_2,
		0,
		MX6Q_PAD_EIM_A24__GPIO_5_4,
		MX6Q_PAD_DISP0_DAT11__GPIO_5_5,
		MX6Q_PAD_DISP0_DAT12__GPIO_5_6,
		MX6Q_PAD_DISP0_DAT13__GPIO_5_7,
		MX6Q_PAD_DISP0_DAT14__GPIO_5_8,
		MX6Q_PAD_DISP0_DAT15__GPIO_5_9,
		MX6Q_PAD_DISP0_DAT16__GPIO_5_10,
		MX6Q_PAD_DISP0_DAT17__GPIO_5_11,
		MX6Q_PAD_DISP0_DAT18__GPIO_5_12,
		MX6Q_PAD_DISP0_DAT19__GPIO_5_13,
		MX6Q_PAD_DISP0_DAT20__GPIO_5_14,
		MX6Q_PAD_DISP0_DAT21__GPIO_5_15,
		MX6Q_PAD_DISP0_DAT22__GPIO_5_16,
		MX6Q_PAD_DISP0_DAT23__GPIO_5_17,
		MX6Q_PAD_CSI0_PIXCLK__GPIO_5_18,
		MX6Q_PAD_CSI0_MCLK__GPIO_5_19,
		MX6Q_PAD_CSI0_DATA_EN__GPIO_5_20,
		MX6Q_PAD_CSI0_VSYNC__GPIO_5_21,
		MX6Q_PAD_CSI0_DAT4__GPIO_5_22,
		MX6Q_PAD_CSI0_DAT5__GPIO_5_23,
		MX6Q_PAD_CSI0_DAT6__GPIO_5_24,
		MX6Q_PAD_CSI0_DAT7__GPIO_5_25,
		MX6Q_PAD_CSI0_DAT8__GPIO_5_26,
		MX6Q_PAD_CSI0_DAT9__GPIO_5_27,
		MX6Q_PAD_CSI0_DAT10__GPIO_5_28,
		MX6Q_PAD_CSI0_DAT11__GPIO_5_29,
		MX6Q_PAD_CSI0_DAT12__GPIO_5_30,
		MX6Q_PAD_CSI0_DAT13__GPIO_5_31,
	},
	['6'-'1'] = {
		MX6Q_PAD_CSI0_DAT14__GPIO_6_0,
		MX6Q_PAD_CSI0_DAT15__GPIO_6_1,
		MX6Q_PAD_CSI0_DAT16__GPIO_6_2,
		MX6Q_PAD_CSI0_DAT17__GPIO_6_3,
		MX6Q_PAD_CSI0_DAT18__GPIO_6_4,
		MX6Q_PAD_CSI0_DAT19__GPIO_6_5,
		MX6Q_PAD_EIM_A23__GPIO_6_6,
		MX6Q_PAD_NANDF_CLE__GPIO_6_7,
		MX6Q_PAD_NANDF_ALE__GPIO_6_8,
		MX6Q_PAD_NANDF_WP_B__GPIO_6_9,
		MX6Q_PAD_NANDF_RB0__GPIO_6_10,
		MX6Q_PAD_NANDF_CS0__GPIO_6_11,
		0,0,
		MX6Q_PAD_NANDF_CS1__GPIO_6_14,
		MX6Q_PAD_NANDF_CS2__GPIO_6_15,
		MX6Q_PAD_NANDF_CS3__GPIO_6_16,
		MX6Q_PAD_SD3_DAT7__GPIO_6_17,
		MX6Q_PAD_SD3_DAT6__GPIO_6_18,
		MX6Q_PAD_RGMII_TXC__GPIO_6_19,
		MX6Q_PAD_RGMII_TD0__GPIO_6_20,
		MX6Q_PAD_RGMII_TD1__GPIO_6_21,
		MX6Q_PAD_RGMII_TD2__GPIO_6_22,
		MX6Q_PAD_RGMII_TD3__GPIO_6_23,
		MX6Q_PAD_RGMII_RX_CTL__GPIO_6_24,
		MX6Q_PAD_RGMII_RD0__GPIO_6_25,
		MX6Q_PAD_RGMII_TX_CTL__GPIO_6_26,
		MX6Q_PAD_RGMII_RD1__GPIO_6_27,
		MX6Q_PAD_RGMII_RD2__GPIO_6_28,
		MX6Q_PAD_RGMII_RD3__GPIO_6_29,
		MX6Q_PAD_RGMII_RXC__GPIO_6_30,
		MX6Q_PAD_EIM_BCLK__GPIO_6_31,
	},
	['7'-'1'] = {
		MX6Q_PAD_SD3_DAT5__GPIO_7_0,
		MX6Q_PAD_SD3_DAT4__GPIO_7_1,
		MX6Q_PAD_SD3_CMD__GPIO_7_2,
		MX6Q_PAD_SD3_CLK__GPIO_7_3,
		MX6Q_PAD_SD3_DAT0__GPIO_7_4,
		MX6Q_PAD_SD3_DAT1__GPIO_7_5,
		MX6Q_PAD_SD3_DAT2__GPIO_7_6,
		MX6Q_PAD_SD3_DAT3__GPIO_7_7,
		MX6Q_PAD_SD3_RST__GPIO_7_8,
		MX6Q_PAD_SD4_CMD__GPIO_7_9,
		MX6Q_PAD_SD4_CLK__GPIO_7_10,
		MX6Q_PAD_GPIO_16__GPIO_7_11,		
		MX6Q_PAD_GPIO_17__GPIO_7_12,
		MX6Q_PAD_GPIO_18__GPIO_7_13,
		0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
	}
};

//static mmap_alloc_t   map_iomux;       /// mmap 관리 구조체
//static unsigned long  iomux_base = 0;

//------------------------------------------------------------------------------
/** @brief   configures a single pad in the iomuxer
    @param   mmap_base
    @param   pad
*///----------------------------------------------------------------------------
static int mxc_iomux_setup_gpio( unsigned long iomux_base, char grp, int gp_nr,  gpio_pulled_t pull_up )
{
	iomux_v3_cfg_t  pad;

	//if ( 0 == iomux_base ) 
	//{
	//	iomux_base = (unsigned long)fa_mmap_alloc( &map_iomux, MX6Q_IOMUXC_BASE_ADDR, MX6Q_IOMUXC_SIZE ); 	
	//}

	if ( ( '1' <= grp ) && ( grp <= '7' ) )
	{
		pad = imx6q_gpio_pads[ grp-'1'][ gp_nr%32 ];
		
		if ( 0 != pad )
		{
			switch( pull_up )
			{
			case GPIO_PULL_UP   : pad &= ~NO_PAD_CTRL; pad |= PAD_CTL_PUS_47K_UP;    break;
			case GPIO_PULL_DN   : pad &= ~NO_PAD_CTRL; pad |= PAD_CTL_PUS_100K_DOWN; break;
			default  : break;
			}

			mxc_iomux_v3_setup_pad( iomux_base, pad );
		}
		else goto err_exit;
	}
	else goto err_exit;
	
	return 0;

err_exit:
	printf( " iMX6 iomux error : invalid gpio [%c-%d]\n", grp, gp_nr );
	return -1;
}


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

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

	return gp->mmap_base + MCU_iMX6_GPIO_DAT_OFS;
}
//------------------------------------------------------------------------------
/** @brief   imx6 gpio 입력 설정
    @param   grp      '1'~'7'
    @param   gp_nr    gp_nr   0..31 
    @param   pull_up  pull_up 
*///----------------------------------------------------------------------------
static void  imx6_gpio_dir_input( char grp, int gp_nr, gpio_pulled_t pull_up  )
{
	imx6_gpio_register_t  *gp = (imx6_gpio_register_t *)(mgpio->private) + (grp - '1');
	
	if ( 0 > imx6_valid_gpio( grp, gp_nr ) ) return;

	// iomux
	mxc_iomux_setup_gpio( gp->mmap_base, grp, gp_nr, pull_up );

	// gpio dir input
	iMX6_GPIO_DIRPORT( gp->mmap_base ) &= ~(1<<gp_nr);	 // input
}
//------------------------------------------------------------------------------
/** @brief   imx6 gpio 출력 설정
    @param   grp      '1'~'7'
    @param   gp_nr    gp_nr   0..31 
    @param   gp_val  gpio 초기 출력값
*///----------------------------------------------------------------------------
static void  imx6_gpio_dir_output( char grp, int gp_nr, int gp_val  )
{
	imx6_gpio_register_t  *gp = (imx6_gpio_register_t *)(mgpio->private) + (grp - '1');
	
	if ( 0 > imx6_valid_gpio( grp, gp_nr ) ) return;

	// iomux
	mxc_iomux_setup_gpio( gp->mmap_base, grp, gp_nr, GPIO_PULL_NONE );

	// gpio dir output
	iMX6_GPIO_DIRPORT( gp->mmap_base ) |= (1<<gp_nr);	 // output
	
	imx6_gpio_output( grp, gp_nr, gp_val );
}
//------------------------------------------------------------------------------
/** @brief   imx6 gpio 입력
    @param   grp      '1'~'7'
    @param   gp_nr    gp_nr   0..31 
*///----------------------------------------------------------------------------
static int  imx6_gpio_input( char grp, int gp_nr  )
{
	imx6_gpio_register_t  *gp = (imx6_gpio_register_t *)(mgpio->private) + (grp - '1');
	unsigned long  rval;
	
	if ( 0 > imx6_valid_gpio( grp, gp_nr ) ) return -1;

	rval = iMX6_GPIO_DATPORT( gp->mmap_base );

	return ( rval & (1<<gp_nr) ) ? 1:0;
}
//------------------------------------------------------------------------------
/** @brief   imx6 gpio 출력
    @param   grp      '1'~'7'
    @param   gp_nr    gp_nr   0..31 
    @param   gpio_val  gpio 초기 출력값
    
*///----------------------------------------------------------------------------
static void  imx6_gpio_output( char grp, int gp_nr, int gp_val  )
{
	imx6_gpio_register_t  *gp = (imx6_gpio_register_t *)(mgpio->private) + (grp - '1');
	unsigned long  rval;
	
	if ( 0 > imx6_valid_gpio( grp, gp_nr ) ) return;

	// gpio data out
	{
		rval = iMX6_GPIO_DATPORT( gp->mmap_base );   
		rval &= ~(1<<gp_nr);
		rval |=  (gp_val&0x1) << gp_nr;
		iMX6_GPIO_DATPORT( gp->mmap_base ) = rval;
	}
}
//------------------------------------------------------------------------------
/** @brief   imx6 gpio close() 함수

*///----------------------------------------------------------------------------
static void  imx6_gpio_close( void )
{
	int lp;
	imx6_gpio_register_t  *gp = (imx6_gpio_register_t *)mgpio->private;
	
	for( lp=0; lp<MCU_iMX6_GPIO_GRP_CNT; lp++, gp++ )
	{
		fa_mmap_free( &gp->map_gpio ); 
	}
	
	// iomux 해제
	if ( mgpio->mmap_base )
	{
		fa_mmap_free( &mgpio->map_info );
		mgpio->mmap_base = 0;	
	}
}
//------------------------------------------------------------------------------
/** @brief   imx6 gpio  초기화
    @param   mcu_nr  mcu 번호
*///----------------------------------------------------------------------------
void  imx6_gpio_open( int  mcu_nr )
{
	mgpio = get_gpio_fops();
	
	mgpio->mcu_nr     = mcu_nr;
	// pin mux mmap
	mgpio->mmap_base  = (unsigned long)fa_mmap_alloc( &mgpio->map_info, MX6Q_IOMUXC_BASE_ADDR, MX6Q_IOMUXC_SIZE );

	// gpio mmap
	{
		int lp;
		imx6_gpio_register_t  *gp = imx6_gpio_register;

		for( lp=0; lp<MCU_iMX6_GPIO_GRP_CNT; lp++, gp++ )
		{
			gp->mmap_base = (unsigned long)fa_mmap_alloc( &gp->map_gpio, gp->phys, MCU_iMX6_GPIO_PHYS_SIZE ); 
		}
	}
		
	mgpio->close           = imx6_gpio_close     ;
	mgpio->dir_input       = imx6_gpio_dir_input ;
	mgpio->dir_output      = imx6_gpio_dir_output;
	mgpio->input           = imx6_gpio_input     ;
	mgpio->output          = imx6_gpio_output    ;
	mgpio->get_input_port  = imx6_get_dat_port   ;
	mgpio->get_output_port = imx6_get_dat_port   ;
	
	mgpio->private   = (void *)imx6_gpio_register;
}