/*------------------------------------------------------------------------------

        code for i2c gpio emulation api v1.0 : hw & os dependent code

        file name       : i2c_gpio_emul_api_dependent.c
        function        : code for i2c gpio emulation api hw & os dependent code
------------------------------------------------------------------------------*/
#include <windows.h>
#include <I2CEMUL_TYPE.h>
#include <I2CEMUL_PDD.h>
#include <bsp_args.h>
#include <oal_i2cemul.h>
#include <bsp_cfg.h>

//===== DEBUGGING MACRO DEFINITION =============================================================================
//#define TEST_MODE
//==============================================================================================================

//===== MACRRO DEFINITION ======================================================================================
#define CLOCKS_PER_ONE_BUSYWAIT 8
//#define IS_DYNAMIC_CPU_CLOCK 1
#define IS_DYNAMIC_CPU_CLOCK 0

#define SCLCON_REG(group)       (*I2CEMULGpioList[group].pSCLCON_REG)
#define SCLPUD_REG(group)       (*I2CEMULGpioList[group].pSCLPUD_REG)
#define SCLDAT_REG(group)       (*I2CEMULGpioList[group].pSCLDAT_REG)
#define SDACON_REG(group)       (*I2CEMULGpioList[group].pSDACON_REG)
#define SDAPUD_REG(group)       (*I2CEMULGpioList[group].pSDAPUD_REG)
#define SDADAT_REG(group)       (*I2CEMULGpioList[group].pSDADAT_REG)

#define SCLCON_INDEX(group) (I2CEMULGpioList[group].dwSCLCONIndex)
#define SCLCON_MASK(group)      (I2CEMULGpioList[group].dwSCLCONMask)
#define SCLPUD_INDEX(group)     (I2CEMULGpioList[group].dwSCLPUDIndex)
#define SCLPUD_MASK(group)      (I2CEMULGpioList[group].dwSCLPUDMask)
#define SCLDAT_INDEX(group)     (I2CEMULGpioList[group].dwSCLDATIndex)

#define SDACON_INDEX(group)     (I2CEMULGpioList[group].dwSDACONIndex)
#define SDACON_MASK(group)      (I2CEMULGpioList[group].dwSDACONMask)
#define SDAPUD_INDEX(group)     (I2CEMULGpioList[group].dwSDAPUDIndex)
#define SDAPUD_MASK(group)      (I2CEMULGpioList[group].dwSDAPUDMask)
#define SDADAT_INDEX(group)     (I2CEMULGpioList[group].dwSDADATIndex)
//==============================================================================================================

//===== GLOBAL VARIABLE DECLARTION =============================================================================
I2C_EMUL_MAPGPIO        *I2CEMULMapGpioLIst[I2C_MAX_GROUP];
I2C_EMUL_UNMAPGPIO      *I2CEMULUnmapGpioList[I2C_MAX_GROUP];
I2CEMUL_GPIO            I2CEMULGpioList[I2C_MAX_GROUP];
//==============================================================================================================

//===== LOCAL FUNCTION DECLARTION ==============================================================================
//==============================================================================================================

//===== EXTERN VARIABLE DECLARTION =============================================================================
//==============================================================================================================

//===== EXTERN FUNCTION DECLARTION =============================================================================
extern void I2CBusyWait(DWORD dwBusyCount);
//==============================================================================================================

BOOL I2C_EMUL_SCLHIGH(BYTE btGroup)
{
        if(I2CEMULGpioList[btGroup].btSCLState != HIGH)
        {
        I2C_EMUL_ConfigSCL(btGroup, SCL_IN);
                I2CEMULGpioList[btGroup].btSCLState = HIGH;
        }
        
        return TRUE;
}

BOOL I2C_EMUL_SCLLOW(BYTE btGroup)
{
        if(I2CEMULGpioList[btGroup].btSCLState != LOW)
        {
        I2C_EMUL_SetSCL(btGroup, LOW);
        I2C_EMUL_ConfigSCL(btGroup, SCL_OUT);
                I2CEMULGpioList[btGroup].btSCLState = LOW;
        }
        
        return TRUE;
}

BOOL I2C_EMUL_SDAHIGH(BYTE btGroup)
{
        if(I2CEMULGpioList[btGroup].btSDAState != HIGH)
        {
        I2C_EMUL_ConfigSDA(btGroup, SCL_IN);
                I2CEMULGpioList[btGroup].btSDAState = HIGH;
        }

        return TRUE;    
}

BOOL I2C_EMUL_SDALOW(BYTE btGroup)
{
        if(I2CEMULGpioList[btGroup].btSDAState != LOW)
        {
        I2C_EMUL_SetSDA(btGroup, LOW);
        I2C_EMUL_ConfigSDA(btGroup, SDA_OUT);
                I2CEMULGpioList[btGroup].btSDAState = LOW;
        }

        return TRUE;    
}

VOID I2C_EMUL_LineInit(BYTE btGroup)
{
        SCLPUD_REG(btGroup) = ( SCLPUD_REG(btGroup) & ~(SCLPUD_MASK(btGroup)<<SCLPUD_INDEX(btGroup)) ) | (0x0<<SCLPUD_INDEX(btGroup));  
        SDAPUD_REG(btGroup) = ( SDAPUD_REG(btGroup) & ~(SDAPUD_MASK(btGroup)<<SDAPUD_INDEX(btGroup)) ) | (0x0<<SDAPUD_INDEX(btGroup));          
        I2CEMULGpioList[btGroup].btSCLState = LOW;
        I2CEMULGpioList[btGroup].btSDAState = LOW;
}

VOID I2C_EMUL_ConfigSCL(BYTE btGroup, BYTE btMode)
{
        if(btMode == SCL_OUT)
        {  
                SCLCON_REG(btGroup) = ( SCLCON_REG(btGroup) & ~(SCLCON_MASK(btGroup)<<SCLCON_INDEX(btGroup)) ) | (0x1<<SCLCON_INDEX(btGroup));     
        }
        else
        {    
                SCLCON_REG(btGroup) = ( SCLCON_REG(btGroup) & ~(SCLCON_MASK(btGroup)<<SCLCON_INDEX(btGroup)) ) | (0x0<<SCLCON_INDEX(btGroup));      
        }
}

VOID I2C_EMUL_ConfigSDA(BYTE btGroup, BYTE btMode)
{
        if(btMode == SDA_OUT) // output
        {  
                SDACON_REG(btGroup) = ( SDACON_REG(btGroup) & ~(SDACON_MASK(btGroup)<<SDACON_INDEX(btGroup)) ) | (0x1<<SDACON_INDEX(btGroup));   
        }
        else // input
        {   
                SDACON_REG(btGroup) = ( SDACON_REG(btGroup) & ~(SDACON_MASK(btGroup)<<SDACON_INDEX(btGroup)) ) | (0x0<<SDACON_INDEX(btGroup));    
        }
}

VOID I2C_EMUL_SetSCL(BYTE btGroup, BYTE btLevel)
{
        if(btLevel == LOW)
        {
                SCLDAT_REG(btGroup) = SCLDAT_REG(btGroup) & ~(0x1<<SCLDAT_INDEX(btGroup));                   
        }
        else
        {
                SCLDAT_REG(btGroup) = SCLDAT_REG(btGroup) | (0x1<<SCLDAT_INDEX(btGroup));              
        }
}

VOID I2C_EMUL_SetSDA(BYTE btGroup, BYTE btLevel)
{
        if(btLevel == LOW)
        {
                SDADAT_REG(btGroup) = SDADAT_REG(btGroup) & ~(0x1<<SDADAT_INDEX(btGroup));               
        }
        else
        {
                SDADAT_REG(btGroup) = SDADAT_REG(btGroup) | (0x1<<SDADAT_INDEX(btGroup));            
        }
}

BYTE I2C_EMUL_GetSCL(BYTE btGroup)
{
        if( SCLDAT_REG(btGroup) & (1<<SCLDAT_INDEX(btGroup)) )
                return 0x01;
        return 0x00;
}

BYTE I2C_EMUL_GetSDA(BYTE btGroup)
{
        if( SDADAT_REG(btGroup) & (1<<SDADAT_INDEX(btGroup)) )
                return 0x01;
        return 0x00;
}

VOID BusyWaitForOneClock(BYTE btGroup)
{
#if CLOCKS_PER_ONE_BUSYWAIT
        DWORD dwBusyCount;
#if IS_DYNAMIC_CPU_CLOCK
        if(I2CEMULGpioList[btGroup].pBSPArgs)
        {
		dwBusyCount = (I2CEMULGpioList[btGroup].pBSPArgs->SystemClocks.ARM_CLK /1000)/I2CEMULGpioList[btGroup].dwSpeed; //need 2 clock per one loop, therefore divide by 500
        }
        else 
        {
                dwBusyCount = (800000000/1000)/I2CEMULGpioList[btGroup].dwSpeed; //need 2 clock per one loop, therefore divide by 500
        }
#else
	if(I2CEMULGpioList[btGroup].pBSPArgs)
	{
		dwBusyCount = (I2CEMULGpioList[btGroup].pBSPArgs->SystemClocks.ARM_CLK /1000)/I2CEMULGpioList[btGroup].dwSpeed; //need 2 clock per one loop, therefore divide by 500
	}
	else
	{
        dwBusyCount = (800000000/1000)/I2CEMULGpioList[btGroup].dwSpeed; //need 2 clock per one loop, therefore divide by 500
	}
#endif

        dwBusyCount /= CLOCKS_PER_ONE_BUSYWAIT;
        I2CBusyWait(dwBusyCount);
#endif
}

VOID GeneralBusyWaitForOneClock(DWORD dwArmClk, DWORD dwSpeed)
{
        DWORD dwBusyCount = (dwArmClk*500)/dwSpeed; //need 2 clock per one loop, therefore divide by 500
        dwBusyCount -= 10; //recorect: call, return, mul, data fetch, div, mov, etc...
        I2CBusyWait(dwBusyCount);
}

VOID BusyWaitForHalfClock(BYTE btGroup)
{
#if CLOCKS_PER_ONE_BUSYWAIT
        DWORD dwBusyCount;
        
#if IS_DYNAMIC_CPU_CLOCK
        if(I2CEMULGpioList[btGroup].pBSPArgs)
        {
		dwBusyCount = (I2CEMULGpioList[btGroup].pBSPArgs->SystemClocks.ARM_CLK /1000)/I2CEMULGpioList[btGroup].dwSpeed; //need 2 clock per one loop, therefore divide by 500
                }
                else
                {
                dwBusyCount = (800000000/1000)/I2CEMULGpioList[btGroup].dwSpeed; //need 2 clock per one loop, therefore divide by 500
        }
#else
	if(I2CEMULGpioList[btGroup].pBSPArgs)
	{
		dwBusyCount = (I2CEMULGpioList[btGroup].pBSPArgs->SystemClocks.ARM_CLK /1000)/I2CEMULGpioList[btGroup].dwSpeed; //need 2 clock per one loop, therefore divide by 500
	}
	else
        {
        dwBusyCount = (800000000/1000)/I2CEMULGpioList[btGroup].dwSpeed; //need 2 clock per one loop, therefore divide by 500
	}
#endif
        
        dwBusyCount /= (CLOCKS_PER_ONE_BUSYWAIT*2);
        I2CBusyWait(dwBusyCount);
#endif
}

VOID GeneralBusyWaitForHalfClock(DWORD dwArmClk, DWORD dwSpeed)
{
        DWORD dwBusyCount = (dwArmClk*250)/dwSpeed; //need 2 clock per one loop, therefore divide by 500
        dwBusyCount -= 10;
        I2CBusyWait(dwBusyCount);
}

VOID BusyWaitForQuarterClock(BYTE btGroup)
{
#if CLOCKS_PER_ONE_BUSYWAIT
        DWORD dwBusyCount;

#if IS_DYNAMIC_CPU_CLOCK
        if(I2CEMULGpioList[btGroup].pBSPArgs)
        {
		dwBusyCount = (I2CEMULGpioList[btGroup].pBSPArgs->SystemClocks.ARM_CLK /1000)/I2CEMULGpioList[btGroup].dwSpeed; //need 2 clock per one loop, therefore divide by 500
                }
                else
                {
                dwBusyCount = (800000000/1000)/I2CEMULGpioList[btGroup].dwSpeed; //need 2 clock per one loop, therefore divide by 500
        }
#else
	if(I2CEMULGpioList[btGroup].pBSPArgs)
	{
                dwBusyCount = (I2CEMULGpioList[btGroup].pBSPArgs->SystemClocks.ARM_CLK /1000)/I2CEMULGpioList[btGroup].dwSpeed; //need 2 clock per one loop, therefore divide by 500
	}
	else
        {
        dwBusyCount = (800000000/1000)/I2CEMULGpioList[btGroup].dwSpeed; //need 2 clock per one loop, therefore divide by 500
	}
        
#endif

        dwBusyCount /= (CLOCKS_PER_ONE_BUSYWAIT*4);
        I2CBusyWait(dwBusyCount);
#endif
}

VOID GeneralBusyWaitForQuarterClock(DWORD dwArmClk, DWORD dwSpeed)
{
        DWORD dwBusyCount = (dwArmClk*125)/dwSpeed; //need 2 clock per one loop, therefore divide by 500
        dwBusyCount -= 10;
        I2CBusyWait(dwBusyCount);
}
