/* 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.
 *
 *****************************************************************************/

/*-----------------------------------------------------------------------------
 *
 * $Author: dtvbm11 $
 * $Date: 2015/11/10 $
 * $RCSfile: sif_if.c,v $
 * $Revision: #1 $
 *
 *---------------------------------------------------------------------------*/

/** @file sif_if.c
 *  This file implement serial interface SIF function.
 */

//---------------------------------------------------------------------------
// Include files
//---------------------------------------------------------------------------
#include "sif_debug.h"
#include "sif_hw.h"
#include "sif_isr.h"
#include "sif_if.h"

#include "drvcust_if.h"
#include "x_assert.h"
#include "x_bim.h"
#include "x_os.h"
#include "x_pinmux.h"
#include "x_hal_arm.h"
#include "x_printf.h"
#include "x_hal_5381.h"

#include "x_pdwnc.h"

//---------------------------------------------------------------------------
// Configurations
//---------------------------------------------------------------------------
/// Use OSAI function option
#if defined (CC_MTK_LOADER)
#define SIF_USE_OSAI            0
#ifdef CC_SIF_USE_INTERRUPT_MODE
#undef CC_SIF_USE_INTERRUPT_MODE
#endif // CC_SIF_USE_INTERRUPT_MODE
#else // defined (CC_MTK_LOADER)
#define SIF_USE_OSAI            1
#endif // defined (CC_MTK_LOADER)


//---------------------------------------------------------------------------
// Constant definitions
//---------------------------------------------------------------------------
#define  SIF_OK    (0)


//---------------------------------------------------------------------------
// Type definitions
//---------------------------------------------------------------------------


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


//---------------------------------------------------------------------------
// Global Variables
//---------------------------------------------------------------------------


//---------------------------------------------------------------------------
// Imported variables
//---------------------------------------------------------------------------


//---------------------------------------------------------------------------
// Imported functions
//---------------------------------------------------------------------------
EXTERN SIF_STRUCT_T* SIF_V1_GetStruct(void);
EXTERN SIF_STRUCT_T* SIF_V2_GetStruct(void);


//---------------------------------------------------------------------------
// Static function forward declarations
//---------------------------------------------------------------------------


//---------------------------------------------------------------------------
// Static variables
//---------------------------------------------------------------------------
static SIF_STRUCT_T* _aprSifStruct[SIF_MAX_NUM_BUS] = {NULL, NULL, NULL, NULL, NULL, NULL};
static SIF_STRUCT_T* _prCurrentActiveSifStruct = NULL;
static BOOL _fgInit = FALSE;
static BOOL _fgInitStart       = FALSE;
static BOOL _fgInitWait        = FALSE;

#if SIF_USE_OSAI
        /// SIF multi-task protection semaphore.
static HANDLE_T _hSifLockSema     = NULL_HANDLE;
#endif

static SIF_CONFIG_T* _prSifConfig = NULL;
static PFN_SIF_CFG_GET_CONFIG_FUNC _pfnSifCfgGetConfigFunc = NULL;   

// This flag will be set upon standby request to avoid SIF opeartion being interrupted
static BOOL _fgSIFDisabled = FALSE;


//---------------------------------------------------------------------------
// Static functions
//---------------------------------------------------------------------------
// Lock SIF semaphore
static VOID _SIF_Lock(VOID)
{
    SIF_Init();
#if SIF_USE_OSAI
    // wait semaphore
    VERIFY(x_sema_lock(_hSifLockSema, X_SEMA_OPTION_WAIT) == OSR_OK);
#endif // SIF_USE_OSAI
}


// Unlock SIF semaphore
static VOID _SIF_Unlock(VOID)
{
#if SIF_USE_OSAI
    // release semaphore
    VERIFY(x_sema_unlock(_hSifLockSema) == OSR_OK);
#endif //SIF_USE_OSAI
}

// Lock SIF semaphore
static VOID _SIF_LockTimeout(UINT32 u4Timeout)
{
    SIF_Init();
#if SIF_USE_OSAI
    // wait semaphore
    VERIFY(x_sema_lock_timeout(_hSifLockSema, u4Timeout) == OSR_OK);
#endif // SIF_USE_OSAI
}


//---------------------------------------------------------------------------
// Inter-file functions
//---------------------------------------------------------------------------
SIF_STRUCT_T* _SIF_GetActiveChannel(void)
{
    return _prCurrentActiveSifStruct;
}

void _SIF_SetActiveChannel(SIF_STRUCT_T* prStruct)
{
    _prCurrentActiveSifStruct = prStruct;
}

#if defined(CC_FAST_INIT)
extern VOID _SIF_DisableAndClearInterrupt(VOID);
//LH for suspend/resume 
void SIF_pm_suspend(void)
{
}
void SIF_pm_resume(void)
{
    Printf("SIF_Master: init\n");

	_SIF_DisableAndClearInterrupt();

	// Enable SIF masters
	IO_WRITE32MSK(BIM_BASE, RW_SM0_CTRL0_REG, SM0EN, SM0EN);
	IO_WRITE32MSK(BIM_BASE, RW_SM0_CTRL0_REG + 0x30, SM0EN, SM0EN);

	if(_SIF_ISR_IsPdwncMasterEnable())
	{
		IO_WRITE32MSK(PDWNC_BASE, RW_SM0_CTRL0_REG - IO_VAL - 0x80, SM0EN, SM0EN);
		
		IO_WRITE32MSK(PDWNC_BASE, RW_AINTEN_REG, SIF_M2_INTEN, SIF_M2_INTEN);
	}
#if !defined(CC_MT5389)		
	if(_SIF_ISR_IsPdwncMaster1Enable())
	{
		
		IO_WRITE32MSK(PDWNC_BASE, RW_SM0_CTRL0_REG - IO_VAL - 0x80 - 0x60, SM0EN, SM0EN);
		IO_WRITE32MSK(PDWNC_BASE, RW_AINTEN_REG, SIF_M3_INTEN, SIF_M3_INTEN);
	}
#endif

}
#endif


//---------------------------------------------------------------------------
// Public functions
//---------------------------------------------------------------------------
//-------------------------------------------------------------------------
/** SIF_Init
 *  Initialize SIF module.
 *  @param  VOID
 *  @return  VOID
 */
//-------------------------------------------------------------------------
VOID SIF_Init(VOID)
{
//    UINT16 u2Ret = 0;
    UINT32 i;
    UINT32 u4Reg;

    u4Reg = HalCriticalStart();

    if (_fgInit)
    {
        HalCriticalEnd(u4Reg);
        return;
    }

    if (!_fgInitStart)
    {
        _fgInitStart = TRUE;
    }
    else
    {
        _fgInitWait = TRUE;
    }
    HalCriticalEnd(u4Reg);
	/*cobra eco IC master0 new mode will use 0xf0029284 bit31:24 & 22:16 ,so this register must be 0 at first*/
	//if(BSP_GetIcVersion() == IC_VER_5398_AB)
	{
		IO_WRITE32MSK(BIM_BASE, RW_SM0_DEV_REG, 0x0, 0xffffffff);
	}
	
    if (!_fgInitWait)
    {
#if SIF_USE_OSAI
        // create semaphore
        VERIFY(x_sema_create(&_hSifLockSema, X_SEMA_TYPE_BINARY, X_SEMA_STATE_UNLOCK) == OSR_OK);
#endif // SIF_USE_OSAI

        // TODO: Query Bus Configuration and save to u4Numbus & au4BusVer
        if((DRVCUST_InitQuery(eSifCfgGetConfigFunc, (UINT32*)(void*)&_pfnSifCfgGetConfigFunc) == 0) &&
            (_pfnSifCfgGetConfigFunc != NULL) )
        {
             _prSifConfig = _pfnSifCfgGetConfigFunc();
        }

        _SIF_ISR_Init();

        for(i = 0; i < (_prSifConfig->u1NumBus>SIF_MAX_NUM_BUS?SIF_MAX_NUM_BUS:_prSifConfig->u1NumBus); i++)
        {
            if(_prSifConfig->au1HwVersion[i] == 2)
            {
                _aprSifStruct[i] = SIF_V2_GetStruct();
                _aprSifStruct[i]->rSifOps.pfn_Init();            
            }
            else if(_prSifConfig->au1HwVersion[i] == 1)
            {     
                _aprSifStruct[i] = SIF_V1_GetStruct();
                _aprSifStruct[i]->rSifOps.pfn_Init();                 
            }
            else
            {
                Printf("Un-recognized H/W version: %d\n", _prSifConfig->au1HwVersion[i]);
                VERIFY(0);
            }
        }    
        _fgInit = TRUE;

    }
    else
    {
        /* Wait until SIF_Init() finish */
        while (!_fgInit)
        {
            static UINT32 u4Count=0;
            x_thread_delay(10);
            u4Count++;
            ASSERT(u4Count < 1000);
        }
    }
#ifdef CC_SIF_OUTPUT_LOW_WAITING
    //change the output level L when SIF master in waiting state
    {
	  UINT32 u4Tmp;
	  u4Tmp=*((volatile unsigned int *)0xF0029240);
	  u4Tmp&=~(1<<6);//clear bit6
	  *((volatile unsigned int *)0xF0029240)=u4Tmp;
    }
#endif
}


//-------------------------------------------------------------------------
/** SIF_Stop
 *  terminate SIF module.
 *  @param  VOID
 *  @return  VOID
 */
//-------------------------------------------------------------------------
#ifdef CC_CLI
VOID SIF_Stop(VOID)
{
    UINT32 i;

    // TODO: Query Bus Configuration and save to u4Numbus & au4BusVer   
    if((DRVCUST_InitQuery(eSifCfgGetConfigFunc, (UINT32*)(void*)&_pfnSifCfgGetConfigFunc) == 0) &&
        (_pfnSifCfgGetConfigFunc != NULL) )
    {
         _prSifConfig = _pfnSifCfgGetConfigFunc();
    }
        
    for(i = 0; i < (_prSifConfig->u1NumBus>SIF_MAX_NUM_BUS?SIF_MAX_NUM_BUS:_prSifConfig->u1NumBus); i++)
    {
        _aprSifStruct[i]->rSifOps.pfn_Stop();
    }
}
#endif

UINT16 SIF_Read(UINT16 u2ClkDiv, UINT8 u1DevAddr, UINT8 u1WordAddr,
                UINT8 *pu1Buf, UINT16 u2ByteCnt)
{
    UINT16 u2Ret = 0;

    u2Ret = SIF_X_Read(SIF_BUS_SYSTEM_I2C, u2ClkDiv, u1DevAddr, 1, u1WordAddr,
                  pu1Buf, u2ByteCnt);
    return u2Ret;
}


/** SIF_ReadNoSubAddr
 *  Read data without word address from SIF module from SIF_BUS_SYSTEM_I2C.
 *  @param  u2ClkDiv    Parameter of divider to divide 27Mhz for SCL.
 *  @param  u1DevAddr   Serial interface device address.
 *  @param  pu1Buf      Pointer to user's buffer.
 *  @param  u2ByteCnt   Number of byte to read. Max = 256 bytes.
 *  @retval > 0         Actual number of bytes read.
 *  @retval = 0         Error
 */
UINT16 SIF_ReadNoSubAddr(UINT16 u2ClkDiv, UINT8 u1DevAddr, UINT8 *pu1Buf,
                         UINT16 u2ByteCnt)
{
    UINT16 u2Ret = 0;
    u2Ret = SIF_X_Read(SIF_BUS_SYSTEM_I2C, u2ClkDiv, u1DevAddr, 0, 0,
                  pu1Buf, u2ByteCnt); 
    return u2Ret;
}


/** SIF_TunerReadNoSubAddr
 *  NIM module api: read data without word address from SIF module from SIF_BUS_TUNER_I2C.
 *  @param  u2ClkDiv    Parameter of divider to divide 27Mhz for SCL.
 *  @param  u1DevAddr   Serial interface device address.
 *  @param  pu1Buf      Pointer to user's buffer.
 *  @param  u2ByteCnt   Number of byte to read. Max = 256 bytes.
 *  @retval > 0         Actual number of bytes read.
 *  @retval = 0         Error
 */
UINT16 SIF_TunerReadNoSubAddr(UINT16 u2ClkDiv, UINT8 u1DevAddr, UINT8 *pu1Buf,
                              UINT16 u2ByteCnt)
{
    UINT16 u2Ret = 0;
    u2Ret = SIF_X_Read(SIF_BUS_TUNER_I2C, u2ClkDiv, u1DevAddr, 0, 0,
                  pu1Buf, u2ByteCnt);
    return u2Ret;
}


/** SIF_Write
 *  Write data with word address to SIF module on SIF_BUS_SYSTEM_I2C.
 *  @param  u2ClkDiv    Parameter of divider to divide 27Mhz for SCL.
 *  @param  u1DevAddr   Serial interface device address.
 *  @param  u1WordAddr  Serial interface word address.
 *  @param  pu1Buf      Pointer to user's buffer.
 *  @param  u2ByteCnt   Number of byte to write. Max = 256 bytes.
 *  @retval > 0         Actual number of bytes written.
 *  @retval = 0         Error
 */
UINT16 SIF_Write(UINT16 u2ClkDiv, UINT8 u1DevAddr, UINT8 u1WordAddr,
                 UINT8 *pu1Buf, UINT16 u2ByteCnt)
{
    UINT16 u2Ret = 0;
    u2Ret = SIF_X_Write(SIF_BUS_SYSTEM_I2C, u2ClkDiv, u1DevAddr, 1, u1WordAddr,
                  pu1Buf, u2ByteCnt);
    return u2Ret;
}


/** SIF_WriteNoSubAddr
 *  Write data without word address to SIF module on SIF_BUS_SYSTEM_I2C.
 *  @param  u2ClkDiv    Parameter of divider to divide 27Mhz for SCL.
 *  @param  u1DevAddr   Serial interface device address.
 *  @param  pu1Buf      Pointer to user's buffer.
 *  @param  u2ByteCnt   Number of byte to write. Max = 256 bytes.
 *  @retval > 0         Actual number of bytes written.
 *  @retval = 0         Error
 */
UINT16 SIF_WriteNoSubAddr(UINT16 u2ClkDiv, UINT8 u1DevAddr, UINT8 *pu1Buf,
                          UINT16 u2ByteCnt)
{
    UINT16 u2Ret = 0;
    u2Ret = SIF_X_Write(SIF_BUS_SYSTEM_I2C, u2ClkDiv, u1DevAddr, 0, 0,
                  pu1Buf, u2ByteCnt);
    return u2Ret;
}


/** SIF_TunerWriteNoSubAddr
 *  NIM module api: write data without word address to SIF module on SIF_BUS_TUNER_I2C.
 *  @param  u2ClkDiv    Parameter of divider to divide 27Mhz for SCL.
 *  @param  u1DevAddr   Serial interface device address.
 *  @param  pu1Buf      Pointer to user's buffer.
 *  @param  u2ByteCnt   Number of byte to write. Max = 256 bytes.
 *  @retval > 0         Actual number of bytes written.
 *  @retval = 0         Error
 */
UINT16 SIF_TunerWriteNoSubAddr(UINT16 u2ClkDiv, UINT8 u1DevAddr, UINT8 *pu1Buf,
                               UINT16 u2ByteCnt)
{
    UINT16 u2Ret = 0;
    u2Ret = SIF_X_Write(SIF_BUS_TUNER_I2C, u2ClkDiv, u1DevAddr, 0, 0,
                  pu1Buf, u2ByteCnt);
    return u2Ret;    
}


/** SIF_ReadMultipleSubAddress
 *  Read data with word address, of which length up to three, from SIF module from SIF_BUS_SYSTEM_I2C.
 *  @param  u2ClkDiv        Parameter of divider to divide 27Mhz for SCL.
 *  @param  u1DevAddr       Serial interface device address.
 *  @param  u1WordAddrNum   Serial interface word address length.
 *  @param  u4WordAddr      Serial interface word address. Format XX B2 B1 B0. B2 is sent on SIF bus first.
 *  @param  pu1Buf          Pointer to user's buffer.
 *  @param  u2ByteCnt       Number of byte to read. Max = 256 bytes.
 *  @retval > 0             Actual number of bytes read.
 *  @retval = 0             Error
 */
UINT16 SIF_ReadMultipleSubAddr(UINT16 u2ClkDiv, UINT8 u1DevAddr, UINT8 u1WordAddrNum, UINT32 u4WordAddr,
                               UINT8 *pu1Buf, UINT16 u2ByteCnt)
{
    UINT16 u2Ret = 0;
    u2Ret = SIF_X_Read(SIF_BUS_SYSTEM_I2C, u2ClkDiv, u1DevAddr, u1WordAddrNum, u4WordAddr,
                  pu1Buf, u2ByteCnt);
    return u2Ret;
}


/** SIF_WriteMultipleSubAddress
 *  Write data with word address, of which length up to three, to SIF module on SIF_BUS_SYSTEM_I2C.
 *  @param  u2ClkDiv        Parameter of divider to divide 27Mhz for SCL.
 *  @param  u1DevAddr       Serial interface device address.
 *  @param  u1WordAddrNum   Serial interface word address length.
 *  @param  u4WordAddr      Serial interface word address. Format XX B2 B1 B0. B2 is sent on SIF bus first.
 *  @param  pu1Buf          Pointer to user's buffer.
 *  @param  u2ByteCnt       Number of byte to read. Max = 256 bytes.
 *  @retval > 0             Actual number of bytes written.
 *  @retval = 0             Error
 */
UINT16 SIF_WriteMultipleSubAddr(UINT16 u2ClkDiv, UINT8 u1DevAddr, UINT8 u1WordAddrNum, UINT32 u4WordAddr,
                                UINT8 *pu1Buf, UINT16 u2ByteCnt)
{
    UINT16 u2Ret = 0;
    u2Ret = SIF_X_Write(SIF_BUS_SYSTEM_I2C, u2ClkDiv, u1DevAddr, u1WordAddrNum, u4WordAddr,
                  pu1Buf, u2ByteCnt);
    return u2Ret;
}


/** SIF_X_Read
 *  Read data with channel ID, word address, of which length up to three, from SIF module.
 *  @param  u1ChannelID     SIF bus ID.
 *  @param  u2ClkDiv        Parameter of divider to divide 27Mhz for SCL.
 *  @param  u1DevAddr       Serial interface device address.
 *  @param  u1WordAddrNum   Serial interface word address length.
 *  @param  u4WordAddr      Serial interface word address. Format XX B2 B1 B0. B2 is sent on SIF bus first.
 *  @param  pu1Buf          Pointer to user's buffer.
 *  @param  u2ByteCnt       Number of byte to read. Max = 256 bytes.
 *  @retval > 0             Actual number of bytes read.
 *  @retval = 0             Error
 */
UINT16 SIF_X_Read(UINT8 u1CHannelID, UINT16 u2ClkDiv, UINT8 u1DevAddr, UINT8 u1WordAddrNum, UINT32 u4WordAddr,
                  UINT8 *pu1Buf, UINT16 u2ByteCnt)
{
    UINT16 u2Ret = 0;

#ifdef CC_FIXED_SIF_CLK_DIV
	UINT32 u4FixClkDiv;
	u4FixClkDiv =DRVCUST_InitGet(eSifcfgFixClkDiv);
	u2ClkDiv=u4FixClkDiv;
#endif
    if(_aprSifStruct[u1CHannelID] == NULL)
    {
        goto X_READ_ERROR;
    }
    if(_aprSifStruct[u1CHannelID]->rSifOps.pfn_X_Read == NULL)
    {
        goto X_READ_ERROR;    
    }
    _SIF_Lock();
    
    if(_fgSIFDisabled)
    {
        _SIF_Unlock();    
        return u2ByteCnt;
    }
    
    _SIF_SetActiveChannel(_aprSifStruct[u1CHannelID]);  
    u2Ret = _aprSifStruct[u1CHannelID]->rSifOps.pfn_X_Read(u1CHannelID, u2ClkDiv, u1DevAddr, u1WordAddrNum, u4WordAddr, pu1Buf, u2ByteCnt);
    _SIF_Unlock();        
X_READ_ERROR:	
    return u2Ret;
}

/** SIF_X_SpecialRead
 *  Read data with channel ID, word address, of which length up to three, from SIF module.
 *  @param  u1ChannelID     SIF bus ID.
 *  @param  u2ClkDiv        Parameter of divider to divide 27Mhz for SCL.
 *  @param  u1DevAddr       Serial interface device address.
 *  @param  u1WordAddrNum   Serial interface word address length.
 *  @param  u4WordAddr      Serial interface word address. Format XX B2 B1 B0. B2 is sent on SIF bus first.
 *  @param  pu1Buf          Pointer to user's buffer.
 *  @param  u2ByteCnt       Number of byte to read. Max = 256 bytes.
 *  @retval > 0             Actual number of bytes read.
 *  @retval = 0             Error
 *   start + slave address + w +tnflg + target address + w +  register address
 *
 restart + slave address + w +tnflg + target address + r
 *    restart + slave address + r +data ... +stop
 */
UINT16 SIF_X_SpecialRead(UINT8 u1CHannelID, UINT16 u2ClkDiv, UINT8 u1DevAddr, UINT8 u1WordAddrNum, UINT32 u4WordAddr,
                  UINT8 *pu1Buf, UINT16 u2ByteCnt)
{
    UINT16 u2Ret = 0;

	
#ifdef CC_FIXED_SIF_CLK_DIV
	UINT32 u4FixClkDiv;
	u4FixClkDiv =DRVCUST_InitGet(eSifcfgFixClkDiv);
	u2ClkDiv=u4FixClkDiv;
#endif
    if(_aprSifStruct[u1CHannelID] == NULL)
    {
        goto X_READ_ERROR;
    }
    if(_aprSifStruct[u1CHannelID]->rSifOps.pfn_X_Sp_Read == NULL)
    {
        goto X_READ_ERROR;    
    }
    _SIF_Lock();
    
    if(_fgSIFDisabled)
    {
        _SIF_Unlock();  
		Printf("SIF_X_Read _fgSIFDisabled:\n");
        return u2ByteCnt;
    }
    
    _SIF_SetActiveChannel(_aprSifStruct[u1CHannelID]);  
    u2Ret = _aprSifStruct[u1CHannelID]->rSifOps.pfn_X_Sp_Read(u1CHannelID, u2ClkDiv, u1DevAddr, u1WordAddrNum, u4WordAddr, pu1Buf, u2ByteCnt);
    _SIF_Unlock();        
X_READ_ERROR:	
    return u2Ret;
}

/** SIF_X_Write
 *  Write data with channel ID, word address, of which length up to three, to SIF module.
 *  @param  u1ChannelID     SIF bus ID.
 *  @param  u2ClkDiv        Parameter of divider to divide 27Mhz for SCL.
 *  @param  u1DevAddr       Serial interface device address.
 *  @param  u1WordAddrNum   Serial interface word address length.
 *  @param  u4WordAddr      Serial interface word address. Format XX B2 B1 B0. B2 is sent on SIF bus first.
 *  @param  pu1Buf          Pointer to user's buffer.
 *  @param  u2ByteCnt       Number of byte to write. Max = 256 bytes.
 *  @retval > 0             Actual number of bytes written.
 *  @retval = 0             Error
 */
UINT16 SIF_X_Write(UINT8 u1CHannelID, UINT16 u2ClkDiv, UINT8 u1DevAddr, UINT8 u1WordAddrNum, UINT32 u4WordAddr,
                   UINT8 *pu1Buf, UINT16 u2ByteCnt)
{
    UINT16 u2Ret = 0;
#ifdef CC_FIXED_SIF_CLK_DIV
		UINT32 u4FixClkDiv;
		u4FixClkDiv =DRVCUST_InitGet(eSifcfgFixClkDiv);
		u2ClkDiv=u4FixClkDiv;
#endif

    if(_aprSifStruct[u1CHannelID] == NULL)
    {
        goto X_WRITE_ERROR;
    }
    if(_aprSifStruct[u1CHannelID]->rSifOps.pfn_X_Write == NULL)
    {
        goto X_WRITE_ERROR;    
    }
    _SIF_Lock();     
    
    if(_fgSIFDisabled)
    {
        _SIF_Unlock();    
        return u2ByteCnt;
    }
    
    _SIF_SetActiveChannel(_aprSifStruct[u1CHannelID]);
    u2Ret = _aprSifStruct[u1CHannelID]->rSifOps.pfn_X_Write(u1CHannelID, u2ClkDiv, u1DevAddr, u1WordAddrNum, u4WordAddr, pu1Buf, u2ByteCnt);
    _SIF_Unlock();    
X_WRITE_ERROR:	
    return u2Ret;
}


UINT16 SIF_X_Write_SpecialDemod(UINT8 u1CHannelID, UINT16 u2ClkDiv, UINT8 u1DevAddr, UINT8 u1WordAddrNum, UINT32 u4WordAddr, UINT8 *pu1Buf, UINT16 u2ByteCnt)
{
    UINT16 u2Ret = 0;
    UINT8 au1Data[4];
    if(_aprSifStruct[u1CHannelID] == NULL)
    {
        goto X_WRITE_SD_ERROR;
    }
    if(_aprSifStruct[u1CHannelID]->rSifOps.pfn_X_Write == NULL)
    {
        goto X_WRITE_SD_ERROR;    
    }
    _SIF_Lock();     
    
    if(_fgSIFDisabled)
    {
        _SIF_Unlock();    
        return u2ByteCnt;
    }
    
    _SIF_SetActiveChannel(_aprSifStruct[u1CHannelID]);
    if(u1WordAddrNum == 1)
    {
        au1Data[0] = (UINT8)(u4WordAddr & 0xFF);
    }    
    if(u1WordAddrNum == 2)
    {
        au1Data[0] = (UINT8)((u4WordAddr >> 8) & 0xFF);
        au1Data[1] = (UINT8)(u4WordAddr & 0xFF);        
    }
    else if(u1WordAddrNum == 3)
    {
        au1Data[0] = (UINT8)((u4WordAddr >> 16) & 0xFF);
        au1Data[1] = (UINT8)((u4WordAddr >> 8) & 0xFF);
        au1Data[2] = (UINT8)(u4WordAddr & 0xFF);        
    }
    else
    {
        _SIF_Unlock();       
        goto X_WRITE_SD_ERROR;        
    }    
    u2Ret = _aprSifStruct[u1CHannelID]->rSifOps.pfn_X_Write(u1CHannelID, u2ClkDiv, u1DevAddr, 0, 0, au1Data, (UINT16)u1WordAddrNum);        
    if(u2Ret != (UINT16)u1WordAddrNum)
    {
        _SIF_Unlock();       
        goto X_WRITE_SD_ERROR;    
    }
    u2Ret = _aprSifStruct[u1CHannelID]->rSifOps.pfn_X_Write(u1CHannelID, u2ClkDiv, u1DevAddr, 0, 0, pu1Buf, u2ByteCnt);    
    _SIF_Unlock();    
X_WRITE_SD_ERROR:              
    return u2Ret;
}


#if 0
#if SIF_USE_OSAI
//-------------------------------------------------------------------------
/** _SIF_AsyncThread
 *  Dedicated thread for async read/write requests.
 *  @param  VOID
 *  @return   VOID
 */
//-------------------------------------------------------------------------
static VOID _SIF_AsyncThread(VOID)
{

    UINT16 u2MsgQIdx, u2Result=0;
    SIZE_T zMsgSize;
    SIF_ASYNC_REQ_T rReq;
    
    while (1)
    {
        zMsgSize = sizeof(SIF_ASYNC_REQ_T);

        // receive request message.
        VERIFY(x_msg_q_receive(&u2MsgQIdx, &rReq, &zMsgSize, &_hSifAsyncMsg, 1, X_MSGQ_OPTION_WAIT) == OSR_OK);
        ASSERT(zMsgSize == sizeof(SIF_ASYNC_REQ_T));

        VERIFY(x_sema_lock(_hSifAsyncSema, X_SEMA_OPTION_WAIT) == OSR_OK);

        if (rReq.u1RequestType == SIF_ASYNC_REQ_READ)
        {
            LOG(7, "AsyncThread call SIF_X_Read: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", rReq.u1CHannelID, rReq.u2ClkDiv, 
                rReq.u1DevAddr, rReq.u1WordAddrNum, rReq.u4WordAddr, rReq.u2ByteCnt);

            u2Result = SIF_X_Read(rReq.u1CHannelID, rReq.u2ClkDiv, rReq.u1DevAddr, rReq.u1WordAddrNum, 
                rReq.u4WordAddr, rReq.pu1Buf, rReq.u2ByteCnt);
        }
        else if (rReq.u1RequestType == SIF_ASYNC_REQ_WRITE)
        {
            LOG(7, "AsyncThread call SIF_X_Write: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", rReq.u1CHannelID, rReq.u2ClkDiv, 
                rReq.u1DevAddr, rReq.u1WordAddrNum, rReq.u4WordAddr, rReq.u2ByteCnt);

            u2Result = SIF_X_Write(rReq.u1CHannelID, rReq.u2ClkDiv, rReq.u1DevAddr, rReq.u1WordAddrNum, 
                rReq.u4WordAddr, rReq.pu1Buf, rReq.u2ByteCnt);
        }

        VERIFY(x_sema_unlock(_hSifAsyncSema) == OSR_OK);

        if (rReq.pfnNfy)
        {
            // call user defined callback function
            rReq.pfnNfy(rReq.pvNfyData, u2Result);
        }
    }    
    
}

/** SIF_AsyncRead
 *  Read data with channel ID, word address, of which length up to three, from SIF module.
 *  @param  u1ChannelID     SIF bus ID.
 *  @param  u2ClkDiv        Parameter of divider to divide 27Mhz for SCL.
 *  @param  u1DevAddr       Serial interface device address.
 *  @param  u1WordAddrNum   Serial interface word address length.
 *  @param  u4WordAddr      Serial interface word address. Format XX B2 B1 B0. B2 is sent on SIF bus first.
 *  @param  pu1Buf          Pointer to user's buffer.
 *  @param  u2ByteCnt       Number of byte to read. Max = 256 bytes.
 *  @param  pfnNfy       Callback function pointer.
 *  @param  pvNfyData       Callback function parameter
 *  @retval > 0             Success
 *  @retval = 0             Error
 */
UINT16 SIF_X_AsyncRead(UINT8 u1CHannelID, UINT16 u2ClkDiv, UINT8 u1DevAddr, UINT8 u1WordAddrNum, UINT32 u4WordAddr,
                  UINT8 *pu1Buf, UINT16 u2ByteCnt, SIF_ASYNC_NFY_FCT pfnNfy, VOID *pvNfyData)
{
    SIF_ASYNC_REQ_T rReq;

    if ( (u1WordAddrNum >= 4) || (!pu1Buf) || (u2ByteCnt <= 0) || (u1CHannelID > 2) )
    {
        Printf("Incorrect parameter!\n");
        return 0;
    }

    rReq.u1RequestType = SIF_ASYNC_REQ_READ;
    rReq.u1CHannelID = u1CHannelID;
    rReq.u2ClkDiv = u2ClkDiv;
    rReq.u1DevAddr = u1DevAddr;
    rReq.u1WordAddrNum = u1WordAddrNum;
    rReq.u4WordAddr= u4WordAddr;
    rReq.pu1Buf = pu1Buf;
    rReq.u2ByteCnt = u2ByteCnt;
    rReq.pfnNfy = pfnNfy;
    rReq.pvNfyData = pvNfyData;

    LOG(7, "Send request to AsyncThread for read: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", rReq.u1CHannelID, rReq.u2ClkDiv, 
        rReq.u1DevAddr, rReq.u1WordAddrNum, rReq.u4WordAddr, rReq.u2ByteCnt);

    // Send request to aync thread
    if (x_msg_q_send(_hSifAsyncMsg, &rReq, sizeof(SIF_ASYNC_REQ_T), 1) != OSR_OK)
    {
        Printf("SIF_X_AsyncRead fail!\n");
        return 0;
    }

    return 1;
}

/** SIF_X_AsyncWrite
 *  Write data with channel ID, word address, of which length up to three, to SIF module.
 *  @param  u1ChannelID     SIF bus ID.
 *  @param  u2ClkDiv        Parameter of divider to divide 27Mhz for SCL.
 *  @param  u1DevAddr       Serial interface device address.
 *  @param  u1WordAddrNum   Serial interface word address length.
 *  @param  u4WordAddr      Serial interface word address. Format XX B2 B1 B0. B2 is sent on SIF bus first.
 *  @param  pu1Buf          Pointer to user's buffer.
 *  @param  u2ByteCnt       Number of byte to write. Max = 256 bytes.
 *  @param  pfnNfy       Callback function pointer.
 *  @param  pvNfyData       Callback function parameter
 *  @retval > 0             Success
 *  @retval = 0             Error
 */
UINT16 SIF_X_AsyncWrite(UINT8 u1CHannelID, UINT16 u2ClkDiv, UINT8 u1DevAddr, UINT8 u1WordAddrNum, UINT32 u4WordAddr,
                  UINT8 *pu1Buf, UINT16 u2ByteCnt, SIF_ASYNC_NFY_FCT pfnNfy, VOID *pvNfyData)
{
    SIF_ASYNC_REQ_T rReq;

    if ( (u1WordAddrNum >= 4) || (!pu1Buf) || (u2ByteCnt <= 0) || (u1CHannelID > 2) )
    {
        Printf("Incorrect parameter!\n");
        return 0;
    }

    rReq.u1RequestType = SIF_ASYNC_REQ_WRITE;
    rReq.u1CHannelID = u1CHannelID;
    rReq.u2ClkDiv = u2ClkDiv;
    rReq.u1DevAddr = u1DevAddr;
    rReq.u1WordAddrNum = u1WordAddrNum;
    rReq.u4WordAddr= u4WordAddr;
    rReq.pu1Buf = pu1Buf;
    rReq.u2ByteCnt = u2ByteCnt;
    rReq.pfnNfy = pfnNfy;
    rReq.pvNfyData = pvNfyData;

    LOG(7, "Send request to AsyncThread for write: 0x%x 0x%x 0x%x 0x%x 0x%x 0x%x\n", rReq.u1CHannelID, rReq.u2ClkDiv, 
        rReq.u1DevAddr, rReq.u1WordAddrNum, rReq.u4WordAddr, rReq.u2ByteCnt);

    // Send request to aync thread
    if (x_msg_q_send(_hSifAsyncMsg, &rReq, sizeof(SIF_ASYNC_REQ_T), 1) != OSR_OK)
    {
        Printf("SIF_X_AsyncWrite fail!\n");
        return 0;
    }

    return 1;
}
#endif  // SIF_USE_OSAI
#endif

#ifdef __MODEL_slt__
INT32 SIF_Diag(VOID)
{
    UINT32 u4Reg = 0;
#ifdef CC_MT5387
    Printf("This is mt5387\n");
#endif
//    u4Reg = _SIF_READ32(RW_SM0_CTRL0_REG);

    Printf("Master 0 %s\n", (u4Reg | SM0EN)? "Enable":"Disable");

    Printf("Bus clock pull high by: %s \n",
           (u4Reg | ODRAIN)? "Master":"Open Drain (pull-high register");

    Printf("SCL-Stretch %s\n", (u4Reg | SCL_STRETCH)? "On":"Off");

    Printf("SCL= %s, SDA= %s\n", (u4Reg | SCL_STATE)?"High":"Low",
           (u4Reg|SDA_STATE) ? "High":"Low");

    return 0;
}
#endif

/** SIF_WaitLastTransaction
 *  Signal SIF driver to stop issueing any SIF read/write and wait the completion of the last SIF transaction
 *  @param  u4Timeout     Safety guard to avoid wait forever.
 *  @retval void
 */
void SIF_WaitLastTransaction(UINT32 u4Timeout)
{
   _SIF_LockTimeout(u4Timeout);
   _fgSIFDisabled = TRUE;
   _SIF_Unlock();   
}

#ifdef CC_SUPPORT_STR
extern VOID _SIF_DisableAndClearInterrupt(VOID);
//-----------------------------------------------------------------------------
/**SIF_pm_str_suspend
 */
//-----------------------------------------------------------------------------
void SIF_pm_str_suspend(void)
{
	_SIF_Lock();
	
	// power off
	CKGEN_WRITE32(0x1CC, CKGEN_READ32(0x1CC) & ~(1U << 11));

	_SIF_Unlock();  
}


//-----------------------------------------------------------------------------
/** SIF_pm_str_resume
 */
//-----------------------------------------------------------------------------
void SIF_pm_str_resume(void)
{
	_SIF_Lock();
	
	CKGEN_WRITE32(0x1CC, CKGEN_READ32(0x1CC) | (1U << 11));
	_fgSIFDisabled = FALSE;
	_SIF_DisableAndClearInterrupt();

	// Enable SIF masters
	IO_WRITE32MSK(BIM_BASE, RW_SM0_CTRL0_REG, SM0EN, SM0EN);
	IO_WRITE32MSK(BIM_BASE, RW_SM0_CTRL0_REG + 0x30, SM0EN, SM0EN);

#ifdef CC_SIF_OUTPUT_LOW_WAITING
	//change the output level L when SIF master in waiting state
	UINT32 u4Tmp;
	u4Tmp=*((volatile unsigned int *)0xF0029240);
	u4Tmp&=~(1<<6);//clear bit6
	*((volatile unsigned int *)0xF0029240)=u4Tmp;
#endif

	_SIF_Unlock();  
}
#endif // CC_SUPPORT_STR

#if !defined (CC_MTK_LOADER) && defined (__KERNEL__)
EXPORT_SYMBOL(SIF_X_Read);
EXPORT_SYMBOL(SIF_X_Write);
#endif

