/* SPDX-License-Identifier: LGPL-2.1-only OR BSD-3-Clause */
/******************************************************************************
 *
 * This file is provided under a dual license.  When you use or
 * distribute this software, you may choose to be licensed under
 * version 2.1 of the GNU Lesser General Public License
 * ("LGPLv2.1 License") or BSD License.
 *
 * LGPLv2.1 License
 *
 * Copyright(C) 2019 MediaTek Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of version 2.1 of the GNU Lesser General Public
 * License as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See https://www.gnu.org/licenses/old-licenses/lgpl-2.1.html for more
 * details.
 *
 * BSD LICENSE
 *
 * Copyright(C) 2019 MediaTek Inc.
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *  * Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *  * Neither the name of the copyright holder nor the names of its
 *    contributors may be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 *****************************************************************************/


/** @file pinmux.c
 *  pinmux.c contains functions to do pin selection & gpio setup.
 */


//-----------------------------------------------------------------------------
// Include files
//-----------------------------------------------------------------------------
#include "x_bim.h"
#include "x_pdwnc.h"
#include "x_pinmux.h"
#include "x_printf.h"
#include "x_hal_5381.h"
#include "x_assert.h"
#include "sif_if.h"
#include "drvcust_if.h"
#include "pdwnc_if.h"
#ifdef CC_MTK_LOADER
#define DEFINE_IS_LOG(level, fmt...)   Printf(fmt)
#else /* CC_MTK_LOADER */
#define DEFINE_IS_LOG	GPIO_IsLog
#endif /* CC_MTK_LOADER */  
#include "x_debug.h"
#include "x_os.h"
#include "drv_common.h"
#include "c_model.h"
#ifndef CC_MTK_LOADER
#ifdef __KERNEL__
#include <linux/module.h>
#ifdef CONFIG_MT53XX_NATIVE_GPIO
#include <linux/soc/mediatek/mtk_gpio.h>
#endif
#endif
#endif

//---------------------------------------------------------------------------
// Types
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
// Configurations
//---------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// Constant definitions
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// Macro definitions
//-----------------------------------------------------------------------------

//-----------------------------------------------------------------------------
// Prototypes
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
// Static variables
//-----------------------------------------------------------------------------
#ifdef CC_MTK_LOADER
static INT32 _GPIO_OwnerNumOffset(UINT32, INT32);
static INT32 _CKGEN_GPIO_Range_Check(INT32 i4GpioNum);
static INT32 _CKGEN_GPIO_Enable(INT32 i4GpioNum, INT32* pfgSet);
static INT32 _CKGEN_GPIO_Output(INT32 i4GpioNum, INT32* pfgSet);
static INT32 _CKGEN_GPIO_Input(INT32 i4GpioNum);
static INT32 _CKGEN_GpioStatus(INT32 i4GpioNum, INT32* pi4Pinmux, INT32* pi4Enable, INT32* pi4Output, INT32* pi4Input);
static INT32 _CKGEN_GPIO_Init(void);

static UINT32 _u4GPIOInit = 0;

static const GPIO_HANDLER_FUNC_TBL gGPIO_HANDLER_FuncTbl[] =
{
    {
        _CKGEN_GPIO_Range_Check,//pfnIsOwner
        _CKGEN_GPIO_Init,		//pfnInit
        _CKGEN_GPIO_Enable,		//pfnEnable
        _CKGEN_GPIO_Input,		//pfnInput
        _CKGEN_GPIO_Output,		//pfnOutput
        NULL, 					//pfnIntrq
        NULL, 					//pfnReg
        NULL,					//pfnStop
        NULL,					//pfnQuery
        _CKGEN_GpioStatus,		//pfnStatus
    },
    {
        PDWNC_GpioRangeCheck,
        NULL,/*_PDWNC_InitGpio, _CKGEN_GPIO_Init->PDWNC_Init->_PDWNC_InitGpio*/
        PDWNC_GpioEnable,
        PDWNC_GpioInput,
        PDWNC_GpioOutput,
        NULL,        
        NULL,
        NULL,
        NULL,
        PDWNC_GpioStatus,
    },
    {
        PDWNC_ServoGpioRangeCheck,
        NULL,
        PDWNC_GpioEnable,
        PDWNC_GpioInput,
        PDWNC_GpioOutput,
        NULL,        
        NULL,
        NULL,
        NULL,
        PDWNC_GpioStatus,
    }
};

//---------------------------------------------------------------------------
// Static functions
//---------------------------------------------------------------------------
static INT32 _GPIO_OwnerNumOffset(UINT32 u4Val, INT32 i4GpioNum)
{
    switch (u4Val)
    {
        case GPIO_HANDLER_CKGEN:
            return i4GpioNum;
        case GPIO_HANDLER_PDWNC_GPIO:
            return i4GpioNum - OPCTRL(0);
        case GPIO_HANDLER_PDWNC_SRVAD:
            if (i4GpioNum == ADIN0)
            {
                return -1;
            }
            else
            {
                return (i4GpioNum - SERVO_0_ALIAS) + ((SERVO_GPIO1 - 1) - OPCTRL(0));
            }
    }
    return i4GpioNum;
}

//-----------------------------------------------------------------------------
/******           GPIO register read/write functions                   *******/
//-----------------------------------------------------------------------------
UINT32 _GPIO_POLL_IN_REG(INT32 i4Idx)
{
    //only for normal gpio
    return GPIO_IN_REG(i4Idx);
}

/*************** CKGEN GPIO Driver Functions *******************************/
static INT32 _CKGEN_GPIO_Range_Check(INT32 i4GpioNum)
{
    if ((i4GpioNum < 0) || (i4GpioNum >= NORMAL_GPIO_NUM))
    {
        return 0;
    }
    else
    {
        return 1;
    }
}

//-----------------------------------------------------------------------------
/** _CKGEN_GPIO_Enable() The GPIO input/output mode setting functions.
 *  *pfgSet == 0 : input
 *  *pfgSet == 1 : output
 *  @param i4GpioNum : the gpio number to set input or output.
 *  @param pfgSet : A integer pointer.  [0 is input, 1 is output]
 *          If it's NULL, this function will return the direction of GPIO(0 or 1).
 *          If it's 0, this function will set the GPIO as input.
 *          If it's 1, this function will set the GPIO as output.
 *  @retval If pfgSet is NULL, it return 0 or 1.
 *          If pfgSet is not NULL, it return *pfgSet.
 *  @NOTE   This function do NOT change GPIO number as GPIO function.
 */
//-----------------------------------------------------------------------------
static INT32 _CKGEN_GPIO_Enable(INT32 i4GpioNum, INT32* pfgSet)
{
    UINT32 u4Val;
    INT32 i4Idx;

    i4Idx = ((UINT32)i4GpioNum >> 5);
    u4Val = GPIO_EN_REG(i4Idx);
    if (pfgSet == NULL)
    {
        return ((u4Val & (1U << (i4GpioNum & GPIO_INDEX_MASK))) ? 1 : 0);
    }
    u4Val = (*pfgSet) ?
            (u4Val | (1U << (i4GpioNum & GPIO_INDEX_MASK))) :
            (u4Val & ~(1U << (i4GpioNum & GPIO_INDEX_MASK)));
    GPIO_EN_WRITE(i4Idx, u4Val);
    return (*pfgSet);
}

//-----------------------------------------------------------------------------
/** _CKGEN_GPIO_Output() The GPIO output value setting functions.
 *  *pfgSet == 0 : low
 *  *pfgSet == 1 : high
 *  @param i4GpioNum : the gpio number to set output value.
 *  @param pfgSet : A integer pointer.  [0 is low, 1 is high]
 *          If it's NULL, this function will return the output value of GPIO(0 or 1).
 *          If it's 0, this function will set output as low.
 *          If it's 1, this function will set output as high.
 *  @retval If pfgSet is NULL, it return 0 or 1.
 *          If pfgSet is not NULL, it return *pfgSet.
 *  @NOTE   This function change GPIO number as GPIO function.(Reg, Input, Output)
 */
//-----------------------------------------------------------------------------
static INT32 _CKGEN_GPIO_Output(INT32 i4GpioNum, INT32* pfgSet)
{
    UINT32 u4Val, u4Val1;
    INT32 i4Idx, i4Val;
    VERIFY(BSP_PinGpioSel(i4GpioNum) == 0);
    i4Idx = ((UINT32)i4GpioNum >> 5);
    if (pfgSet == NULL)	//NULL: for query gpio status, must be GPIO output , but not change pin level
    {
        if (_CKGEN_GPIO_Enable(i4GpioNum, NULL) == 0) //old is input state, change to be output
        {
            //get pin level
            u4Val1 = GPIO_IN_REG(i4Idx);
            //get current out register setting
            u4Val = GPIO_OUT_REG(i4Idx);
            u4Val = (u4Val1 & (1U << (i4GpioNum & GPIO_INDEX_MASK))) ?
                    (u4Val | (1U << (i4GpioNum & GPIO_INDEX_MASK))) :
                    (u4Val & ~(1U << (i4GpioNum & GPIO_INDEX_MASK)));
            GPIO_OUT_WRITE(i4Idx, u4Val);
            // Set the output mode.
            i4Val = 1;
            _CKGEN_GPIO_Enable(i4GpioNum, &i4Val);
        }
        u4Val = GPIO_OUT_REG(i4Idx);
        return ((u4Val & (1U << (i4GpioNum & GPIO_INDEX_MASK))) ? 1 : 0);
    }

    u4Val = GPIO_OUT_REG(i4Idx);
    u4Val = (*pfgSet) ?
            (u4Val | (1U << (i4GpioNum & GPIO_INDEX_MASK))) :
            (u4Val & ~(1U << (i4GpioNum & GPIO_INDEX_MASK)));

    GPIO_OUT_WRITE(i4Idx, u4Val);
    // Set the output mode.
    i4Val = 1;
    UNUSED(_CKGEN_GPIO_Enable(i4GpioNum, &i4Val));

    return (*pfgSet);
}

//-----------------------------------------------------------------------------
/** _CKGEN_GPIO_Input() The GPIO intput value reading functions.
 *  @param i4GpioNum : the gpio number to get intput value.
 *  @retval 0 or 1.  (0 is low, 1 is high).
 *  @NOTE   This function change GPIO number as GPIO function.(Reg, Input, Output)
 */
//-----------------------------------------------------------------------------
static INT32 _CKGEN_GPIO_Input(INT32 i4GpioNum)
{
    UINT32 u4Val;
    INT32 i4Idx;

    VERIFY(BSP_PinGpioSel(i4GpioNum) == 0);

    // Read input value.
    i4Idx = ((UINT32)i4GpioNum >> 5);
    u4Val = GPIO_IN_REG(i4Idx);

    return ((u4Val & (1U << (i4GpioNum & GPIO_INDEX_MASK))) ? 1U : 0);

}

static INT32 _CKGEN_GpioStatus(INT32 i4GpioNum, INT32* pi4Pinmux, INT32* pi4Enable, INT32* pi4Output, INT32* pi4Input)
{
    UINT32 u4Val;
    INT32 i4Idx = 0;

    *pi4Pinmux = BSP_PinGpioGet(i4GpioNum, &u4Val);

    i4Idx = ((UINT32)i4GpioNum >> 5);

    u4Val = GPIO_EN_REG(i4Idx);
    *pi4Enable = ((u4Val & (1U << (i4GpioNum & GPIO_INDEX_MASK))) ? 1 : 0);

    u4Val = GPIO_OUT_REG(i4Idx);
    *pi4Output = ((u4Val & (1U << (i4GpioNum & GPIO_INDEX_MASK))) ? 1 : 0);

    u4Val = GPIO_IN_REG(i4Idx);
    *pi4Input = ((u4Val & (1U << (i4GpioNum & GPIO_INDEX_MASK))) ? 1U : 0);


    return 0;
}

/***************************************************************/
static INT32 _CKGEN_GPIO_Init()
{
    INT32 i4Ret;
    INT32 i4Idx;
    // clear all edge triggered interrupt status 0.
    for (i4Idx = 0; i4Idx < GPIO_INT_INDEX_MAX; i4Idx++)
    {
		vIO32Write4B(_GPIO_EXTINT_REG(i4Idx), 0);
    }
    i4Ret = 0;

    i4Ret = PDWNC_Init();//why we put PDWNC_Init() here...?
    {
        PFN_GPIO_TIMERINIT_FUNC pfnTimerInit = NULL;

        // call driver customization loader env init function.
        DRVCUST_InitQueryUptr(eGpiotimerInitFunc, (UPTR *)(void*)&pfnTimerInit);
        if (pfnTimerInit != NULL)
        {
            // call driver customization gpio timer init function.
            pfnTimerInit();
        }
    }
    return i4Ret;
}
#endif

//-----------------------------------------------------------------------------
/** GPIO_Enable() The GPIO input/output mode setting functions.
 *  It will check the i4GpioNum and set to related register bit as 0 or 1.
 *  0 is input mode and 1 is output mode.
 *  @param i4GpioNum the gpio number to set or read.
 *  @param pfgSet A integer pointer.
 *         If it's NULL, this function will return the current mode of gpio number (0 or 1).
 *         If it's not NULL, it must reference to a integer.
 *         If the integer is 0, this function will set the mode of the gpio number as input mode,
 *         otherwise set as output mode.
 *  @retval If pfgSet is NULL, it return 0 or 1 (0 is input mode, 1 is output mode.)
 *          Otherwise, return (*pfgSet).
 */
//-----------------------------------------------------------------------------
INT32 GPIO_Enable(INT32 i4GpioNum, INT32* pfgSet)
{
#ifdef CC_MTK_LOADER
    UINT32 u4Val;

    for (u4Val = 0; u4Val < (sizeof(gGPIO_HANDLER_FuncTbl) / sizeof(GPIO_HANDLER_FUNC_TBL)); u4Val++)
    {
        if (gGPIO_HANDLER_FuncTbl[u4Val].pfnIsOwner(i4GpioNum))
        {
            if (gGPIO_HANDLER_FuncTbl[u4Val].pfnEnable != NULL)
            {
                return  gGPIO_HANDLER_FuncTbl[u4Val].pfnEnable(_GPIO_OwnerNumOffset(u4Val, i4GpioNum), pfgSet);
            }
            else
            {
                LOG(0, "The enable function of this module is NULL\n");
            }
        }
    }
	return -1;
#else
	return mtk_gpio_enable(i4GpioNum,pfgSet);
#endif
    
}

//-----------------------------------------------------------------------------
/** GPIO_Output() The GPIO output value setting functions.
 *  It will check the i4GpioNum and set to related register bit as 0 or 1.
 *  @param i4GpioNum the gpio number to set or read.
 *  @param pfgSet A integer pointer.
 *          If it's NULL, this function will return the bit of gpio number (0 or 1).
 *          If it's not NULL, it must reference to a integer.
 *          If the integer is 0, this function will set the bit of the gpio number as 0,
 *          otherwise set as 1.
 *  @retval If pfgSet is NULL, it return 0 or 1 (the bit value of the gpio number of output mode.
 *          Otherwise, return (*pfgSet).
 */
//-----------------------------------------------------------------------------
INT32 GPIO_Output(INT32 i4GpioNum, INT32* pfgSet)
{
#ifdef CC_MTK_LOADER
    UINT32 u4Val;

    for(u4Val = 0; u4Val < (sizeof(gGPIO_HANDLER_FuncTbl) / sizeof(GPIO_HANDLER_FUNC_TBL)); u4Val++)
    {
        if (gGPIO_HANDLER_FuncTbl[u4Val].pfnIsOwner(i4GpioNum))
        {
            if (gGPIO_HANDLER_FuncTbl[u4Val].pfnOutput != NULL)
            {
                return  gGPIO_HANDLER_FuncTbl[u4Val].pfnOutput(_GPIO_OwnerNumOffset(u4Val, i4GpioNum), pfgSet);
            }
            else
            {
                LOG(0, "The output function of this module is NULL\n");
            }
        }
    }
	return -1;
#else
	VERIFY(BSP_PinGpioSel(i4GpioNum) == 0);
	return mtk_gpio_output(i4GpioNum,pfgSet);
#endif
    
}

//-----------------------------------------------------------------------------
/** GPIO_Input()  The GPIO input reading functions.
 *  It will check the i4GpioNum and read related register bit to return.
 *  @param i4GpioNum the gpio number to read.
 *  @retval 0 or 1.  (GPIO input value)(0 is low, 1 is high).
 */
//-----------------------------------------------------------------------------
INT32 GPIO_Input(INT32 i4GpioNum)
{
#ifdef CC_MTK_LOADER
    UINT32 u4Val;

    for (u4Val = 0; u4Val < (sizeof(gGPIO_HANDLER_FuncTbl) / sizeof(GPIO_HANDLER_FUNC_TBL)); u4Val++)
    {
        if (gGPIO_HANDLER_FuncTbl[u4Val].pfnIsOwner(i4GpioNum))
        {
            if (gGPIO_HANDLER_FuncTbl[u4Val].pfnInput != NULL)
            {
                return  gGPIO_HANDLER_FuncTbl[u4Val].pfnInput(_GPIO_OwnerNumOffset(u4Val, i4GpioNum));
            }
            else
            {
                LOG(0, "The input function of this module is NULL\n");
            }
        }
    }
    return -1;
#else
	VERIFY(BSP_PinGpioSel(i4GpioNum) == 0);
	return mtk_gpio_get_value(i4GpioNum);
#endif
}

INT32 GPIO_Status(INT32 i4GpioNum, INT32* pi4Pinmux, INT32* pi4Enable, INT32* pi4Output, INT32* pi4Input)
{
	UINT32 u4Val;
#ifdef CC_MTK_LOADER
    for (u4Val = 0; u4Val < (sizeof(gGPIO_HANDLER_FuncTbl) / sizeof(GPIO_HANDLER_FUNC_TBL)); u4Val++)
    {
        if (gGPIO_HANDLER_FuncTbl[u4Val].pfnIsOwner(i4GpioNum))
        {
            if (gGPIO_HANDLER_FuncTbl[u4Val].pfnStatus != NULL)
            {
                return  gGPIO_HANDLER_FuncTbl[u4Val].pfnStatus(_GPIO_OwnerNumOffset(u4Val, i4GpioNum), pi4Pinmux, pi4Enable, pi4Output, pi4Input);
            }
            else
            {
                LOG(0, "The input function of this module is NULL\n");
            }
        }
    }
    return -1;
#else
	*pi4Pinmux = BSP_PinGpioGet(i4GpioNum, &u4Val);
	return mtk_gpio_status(i4GpioNum,NULL,pi4Enable,pi4Output,pi4Input);
#endif
}

#ifndef CC_MTK_LOADER
//-----------------------------------------------------------------------------
/** GPIO_Intrq() The GPIO interrupt enable setting functions. It will check
 *  the i4GpioNum and set to related register bit as 0 or 1.  In this
 *  function, 0 is interrupt disable mode and 1 is interrupt enable mode.
 *  @param i4GpioNum the gpio number to set or read.
 *  @param pfgSet A integer pointer.  If it's NULL, this function will return
 *  the current setting of gpio number (0 or 1).  If it's not NULL, it must
 *  reference to a integer.  If the integer is 0, this function will set the
 *  mode of the gpio number as interrupt disable mode, otherwise set as
 *  interrupt enable mode.
 *  @retval If pfgSet is NULL, it return 0 or 1 (0 is interrupt disable mode,
 *          1 is interrupt enable mode.)  Otherwise, return (*pfgSet).
 */
//-----------------------------------------------------------------------------
INT32 GPIO_Intrq(INT32 i4GpioNum, INT32* pfgSet)
{
    return mtk_gpio_set_irq(i4GpioNum,pfgSet);
}

void _GPIO_Reg_Proc(int gpio, int status, void* data)
{
    PFN_GPIO_CALLBACK pfnCallback = (PFN_GPIO_CALLBACK)data;
    pfnCallback(gpio, status);
}

INT32 GPIO_Reg(INT32 i4Gpio, GPIO_TYPE eType, PFN_GPIO_CALLBACK pfnCallback)
{
    // Calling kernel GPIO.
    return mtk_gpio_request_irq(i4Gpio, (MTK_GPIO_IRQ_TYPE)eType, _GPIO_Reg_Proc, pfnCallback);
}
#endif
//-----------------------------------------------------------------------------
/** GPIO_SetOut() to set gpio output value.
 *  @param i4GpioNum
 *  @param i4Val 0 or 1.
 *  @retval to return current gpio out register setting.
 */
//-----------------------------------------------------------------------------
INT32 GPIO_SetOut(INT32 i4GpioNum, INT32 i4Val)
{
    INT32 fgSet, i4Ret;

    fgSet = i4Val;
    i4Ret = GPIO_Output(i4GpioNum, &fgSet);
    if (i4Ret != i4Val)
    {
        return i4Ret;
    }
    return GPIO_Output(i4GpioNum, NULL);
}
//-----------------------------------------------------------------------------
/** GPIO_Input()  The GPIO input reading functions. It will check the
 *  i4GpioNum and read related register bit to return.
 *  @param i4GpioNum the gpio number to read.
 *  @retval 0 or 1.  (GPIO input value.)
 */
//-----------------------------------------------------------------------------
INT32 GPIO_GetIn(INT32 i4GpioNum)
{
#ifdef CC_MTK_LOADER
    INT32 i4Mode;

    // Set to input mode.
    i4Mode = 0;
    UNUSED(GPIO_Enable(i4GpioNum, &i4Mode));
#endif

    return GPIO_Input(i4GpioNum);

}
//-----------------------------------------------------------------------------
// GPIO Interface functions.
//-----------------------------------------------------------------------------
INT32 GPIO_Init()
{
#ifdef CC_MTK_LOADER
    UINT32 u4Val;

    if (_u4GPIOInit)
    {
        return 0;
    }
    _u4GPIOInit = 1;
    for (u4Val = 0; u4Val < (sizeof(gGPIO_HANDLER_FuncTbl) / sizeof(GPIO_HANDLER_FUNC_TBL)); u4Val++)
    {
        if (gGPIO_HANDLER_FuncTbl[u4Val].pfnInit != NULL)
        {
            gGPIO_HANDLER_FuncTbl[u4Val].pfnInit();
        }
        else
        	LOG(0, "The init function of this module is NULL\n");
    }
#endif
    return 0;
}

INT32 GPIO_Stop()
{
#ifdef CC_MTK_LOADER
    UINT32 u4Val;

    for (u4Val = 0; u4Val < (sizeof(gGPIO_HANDLER_FuncTbl) / sizeof(GPIO_HANDLER_FUNC_TBL)); u4Val++)
    {
        if (gGPIO_HANDLER_FuncTbl[u4Val].pfnStop != NULL)
        {
            return  gGPIO_HANDLER_FuncTbl[u4Val].pfnStop();
        }
        else
        {
            LOG(0, "The stop function of this module is NULL\n");
        }
    }
    return -1;
#else
	return mtk_gpio_stop();
#endif
}

INT32 GPIO_Query(INT32 i4Gpio, INT32* pi4Intr, INT32* pi4Fall, INT32* pi4Rise)
{
#ifdef CC_MTK_LOADER
    UINT32 u4Val;

    for (u4Val = 0; u4Val < (sizeof(gGPIO_HANDLER_FuncTbl) / sizeof(GPIO_HANDLER_FUNC_TBL)); u4Val++)
    {
        if (gGPIO_HANDLER_FuncTbl[u4Val].pfnIsOwner(i4Gpio))
        {
            if (gGPIO_HANDLER_FuncTbl[u4Val].pfnQuery != NULL)
            {
                return  gGPIO_HANDLER_FuncTbl[u4Val].pfnQuery(_GPIO_OwnerNumOffset(u4Val, i4Gpio), pi4Intr, pi4Fall, pi4Rise);
            }
            else
            {
                LOG(0, "The query function of this module is NULL\n");
            }
        }
    }
    return -1;
#else
	return mtk_gpio_query(i4Gpio,pi4Intr,pi4Fall,pi4Rise);
#endif
}

#ifdef CC_SUPPORT_STR
static  UINT32 GPIOOUT[TOTAL_GPIO_IDX];
static	UINT32 GPIOEN[TOTAL_GPIO_IDX];
static	UINT32 ED2INTEN[GPIO_INT_INDEX_MAX];
static	UINT32 LEVINTEN[GPIO_INT_INDEX_MAX];
static	UINT32 ENTPOL[GPIO_INT_INDEX_MAX];
static	UINT32 EXTINTEN[GPIO_INT_INDEX_MAX];
static	UINT32 EXTINT[GPIO_INT_INDEX_MAX];
static	UINT32 PINMUX[CKGEN_PIN_REG];
static	UINT32 E2STRENGTH[GPIO_E2_REG_NUM];
static	UINT32 E4STRENGTH[GPIO_E4_REG_NUM];
static	UINT32 E8STRENGTH[GPIO_E8_REG_NUM];
static	UINT32 PUPDSTRENGTH[GPIO_PUPD_REG_NUM];
static	UINT32 R0STRENGTH[GPIO_R0_REG_NUM];
static	UINT32 R1STRENGTH[GPIO_R1_REG_NUM];
static	UINT32 SDSTRENGTH;
//static	UINT32 OTHERSTRENGTH[CKGEN_PIN_REG];
static	UINT32 u4IRQST, u4IRQEN, u4MINTST, u4MINTEN;
//-----------------------------------------------------------------------------
/** GPIO_pm_str_suspend
 */
//-----------------------------------------------------------------------------
void GPIO_pm_str_suspend(void)
{
	UINT32 i;
	for (i = 0; i < TOTAL_GPIO_IDX; i++)
	{
		GPIOOUT[i] = GPIO_OUT_REG(i);
		GPIOEN[i] = GPIO_EN_REG(i);
	}
	for (i = 0; i < GPIO_INT_INDEX_MAX; i++)
	{
		ED2INTEN[i] = u4IO32Read4B(_GPIO_ED2INTEN_REG(i));
		LEVINTEN[i] = u4IO32Read4B(_GPIO_LEVINTEN_REG(i));
		ENTPOL[i] = u4IO32Read4B(_GPIO_ENTPOL_REG(i));
		EXTINTEN[i] = u4IO32Read4B(_GPIO_EXTINTEN_REG(i));
		EXTINT[i] = u4IO32Read4B(_GPIO_EXTINT_REG(i));


	}
	for (i = 0; i < CKGEN_PIN_REG; i++)
	{
		PINMUX[i] = u4IO32Read4B(_GPIO_PMUX_REG(i));
	}
	// skip EMMC IO Driving, EMMC driver will control it
	// SD
	SDSTRENGTH = u4IO32Read4B(CKGEN_REG_MSDCPAD_DRV_CFG0);
	for(i = 0; i < GPIO_E2_REG_NUM; i++)
	{
		E2STRENGTH[i] = u4IO32Read4B(_GPIO_E2_REG(i));
		E4STRENGTH[i] = u4IO32Read4B(_GPIO_E4_REG(i));
		E8STRENGTH[i] = u4IO32Read4B(_GPIO_E8_REG(i));
	}
	for(i = 0; i < GPIO_PUPD_REG_NUM; i++)
	{
		PUPDSTRENGTH[i] = u4IO32Read4B(_GPIO_PUPD_REG(i));
		R0STRENGTH[i] = u4IO32Read4B(_GPIO_R0_REG(i));
		R1STRENGTH[i] = u4IO32Read4B(_GPIO_R1_REG(i));
	}
	u4IRQST = BIM_REG32(REG_IRQST);
	u4IRQEN = BIM_REG32(REG_IRQEN);
	u4MINTST = BIM_REG32(REG_MISC_IRQST);
	u4MINTEN = BIM_REG32(REG_MISC_IRQEN);
}


//-----------------------------------------------------------------------------
/** GPIO_pm_str_resume
 */
//-----------------------------------------------------------------------------
void GPIO_pm_str_resume(void)
{
	UINT32 i;
	unsigned long flags;

    // skip GPIO 45/43/44, usb driver will control it
	GPIOOUT[1] = (GPIOOUT[1]&0xFFFFC7FF) | (GPIO_OUT_REG(1)&0x00003800);
	GPIOEN[1] = (GPIOEN[1]&0xFFFFC7FF) | (GPIO_EN_REG(1)&0x00003800);
	
	for (i = 0; i < TOTAL_GPIO_IDX; i++)
	{
		GPIO_OUT_WRITE(i, GPIOOUT[i]);
		GPIO_EN_WRITE(i, GPIOEN[i]);
	}
	for (i = 0; i < GPIO_INT_INDEX_MAX; i++)
	{
		vIO32Write4B(_GPIO_ED2INTEN_REG(i), ED2INTEN[i]);
		vIO32Write4B(_GPIO_LEVINTEN_REG(i), LEVINTEN[i]);
		vIO32Write4B(_GPIO_ENTPOL_REG(i), ENTPOL[i]);
		vIO32Write4B(_GPIO_EXTINTEN_REG(i), EXTINTEN[i]);
		vIO32Write4B(_GPIO_EXTINT_REG(i), EXTINT[i]);

	}

	#ifndef CC_MTK_LOADER
	mtk_gpio_str_wait_queue(&flags);
	#endif

	#if !defined(CC_MTK_LOADER) && defined(CC_MT5895)
	// skip msdc gpio, msdc driver will control it
	PINMUX[14] = (PINMUX[14]&0x0000FFFF) | (PINMUX14_REG()&0xFFFF0000);
	PINMUX[15] = (PINMUX15_REG()&0xFFFFFFFF);
	#endif
	for (i = 0; i < CKGEN_PIN_REG; i++)
	{
		vIO32Write4B(_GPIO_PMUX_REG(i), PINMUX[i]);
	}
	// skip EMMC IO Driving, EMMC driver will control it
	//SD
	SDSTRENGTH = (SDSTRENGTH & 0x3FFFF) | (u4IO32Read4B(CKGEN_REG_MSDCPAD_DRV_CFG0)&0xFFFC0000); //bit0-bit17
	vIO32Write4B(CKGEN_REG_MSDCPAD_DRV_CFG0, SDSTRENGTH);
	for (i = 0; i < GPIO_E2_REG_NUM; i++)
	{
		vIO32Write4B(_GPIO_E2_REG(i), E2STRENGTH[i]);
		vIO32Write4B(_GPIO_E4_REG(i), E4STRENGTH[i]);
		vIO32Write4B(_GPIO_E8_REG(i), E8STRENGTH[i]);
	}
	for(i = 0; i < GPIO_PUPD_REG_NUM; i++)
	{
		vIO32Write4B(_GPIO_PUPD_REG(i), PUPDSTRENGTH[i]);
		vIO32Write4B(_GPIO_R0_REG(i), R0STRENGTH[i]);
		vIO32Write4B(_GPIO_R1_REG(i), R1STRENGTH[i]);
	}
	#ifndef CC_MTK_LOADER
	mtk_gpio_str_release_queue(flags);
	#endif
	BIM_WRITE32(REG_RW_IRQCL, u4IRQST);
	BIM_WRITE32(REG_IRQEN, u4IRQEN);
	BIM_WRITE32(REG_MISC_IRQCLR, u4MINTST);
	BIM_WRITE32(REG_MISC_IRQEN, u4MINTEN);
}
#endif

