/* 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:faun.li$
 * $Date$
 * $RCSfile: sif_hw.c,v $
 * $Revision$
 *
 *---------------------------------------------------------------------------*/

/** @file sif_hw.c
 *  This file implement serial interface common 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_timer.h"

#include "x_pdwnc.h"
//---------------------------------------------------------------------------
// Configurations
//---------------------------------------------------------------------------

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


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


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

//cause ctrl* registers of master* have the same layout
//so we just use sm0 define as common define

#define I2C_CTL0_SCL_STRETCH           (FLD_SM0_SCL_STRECH)
#define I2C_CTL0_EN                    (FLD_SM0_EN)
#define I2C_CTL0_SDA_STATE             (FLD_SM0_SDA_STATE)
#define I2C_CTL0_SCL_STATE             (FLD_SM0_SCL_STATE)
#define I2C_CTL0_CS_STATUS             (FLD_SM0_CS_STATUS)
#define I2C_CTL0_DEG_EN                (FLD_SM0_DEG_EN)
#define I2C_CTL0_WAIT_LEVEL            (FLD_SM0_WAIT_LEVEL)
#define I2C_CTL0_DEG_CNT               (FLD_SM0_DEG_CNT)
#define I2C_CTL0_CLK_DIV               (FLD_SM0_CLK_DIV)
#define I2C_CTL0_VSYNC_MODE            (FLD_SM0_VSYNC_MODE)
#define I2C_CTL0_ODRAIN                (FLD_SM0_ODRAIN)

#define I2C_CTL1_TRI                   (FLD_SM0_TRI)
#define I2C_CTL1_MODE                  (FLD_SM0_MODE)
#define I2C_CTL1_PGLEN                 (FLD_SM0_PGLEN)
#define I2C_CTL1_ACK0                  (FLD_SM0_ACK0)

#define I2C_CTL2_SCL_MODE              (FLD_SM0_SCL_MODE)
#define I2C_CTL2_SCL_RISE_VALUE        (FLD_SM0_SCL_RISE_VALUE)

#define I2C_CTL3_ACT_MODE              (Fld(7,0,AC_MSKB0))
//use register map style define
#define I2C_CTL3_SDEV_MODE             (FLD_SM0_SDEV_MODE)
#define I2C_CTL3_STR_RDNACK_STOP_MODE  (FLD_SM0_STR_RDNACK_STOP_MODE)
#define I2C_CTL3_STR_RDACK_MODE        (FLD_SM0_STR_RDACK_MODE)
#define I2C_CTL3_RDNACK_STOP_MODE      (FLD_SM0_RDNACK_STOP_MODE)
#define I2C_CTL3_STR_WD_STOP_MODE      (FLD_SM0_STR_WD_STOP_MODE)
#define I2C_CTL3_STR_WD_MODE           (FLD_SM0_STR_WD_MODE)
#define I2C_CTL3_WD_STOP_MODE          (FLD_SM0_WD_STOP_MODE)
#define I2C_CTL3_ACK1                  (FLD_SM0_ACK1)
#define I2C_CTL3_SACK                  (FLD_SM0_ACK1_DEVADDR_ACK)
#define I2C_SDEV_ADDR                  (FLD_SM0_DEV_ADDR)

//both loader and kernek i2c driver uses the same defines 
#define MODE_START      		(0x01)
#define MODE_WRITE      		(0x02)
#define MODE_STOP       		(0x03)
#define MODE_READ_NACK  		(0x04)
#define MODE_READ       		(0x05)

#define I2C_OK                	(0)
#define I2C_ERROR_NODEV       	(-1)       // I2C device doesn't ack to SIF master
#define I2C_ERROR_SUBADDR     	(-2)       // There is error when I2C master transmits subaddress data
#define I2C_ERROR_DATA        	(-3)       // There is error when I2C master transmits/receives data
#define I2C_ERROR_BUS_BUSY    	(-4)       // I2C bus is busy, can't access
#define I2C_ERROR_PARAM       	(-5)       // Function parameter is invalid
#define I2C_ERROR_TIMEOUT     	(-6)       // I2C device timeout

// mode BIT(0), 1:send start, 0: no start.
// mode BIT(1), 1:send stop,  0: no stop.
#define IF_START(x)				((x & 1)==1)
#define IF_STOP(x)				((x & 2)==2)
#define IF_START_STOP(x)		((x & 3)==3)

#define NEED_NONE				(0)
#define NEED_START				(1)
#define NEED_STOP				(2)
#define NEED_START_STOP			(3)
#define SIF_Fld(wid,shft) (((UINT32)wid<<8)|shft)

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


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


//---------------------------------------------------------------------------
// Imported functions
//---------------------------------------------------------------------------

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

//---------------------------------------------------------------------------
// Static variables
//---------------------------------------------------------------------------
//UINT8 strentch;
//UINT8 devmod;	
//UINT32 u4RegOffset;
//I2CREG_T I2cReg;

static I2CMAST_T arMasters[SIF_MAX_ALL_ICS_MASTER] =
{
    {
		.strentch = 0,
    	.devmod = 0,
    	.base =  IO_SIF_BASE,
    	{       // i2c0
		.ctl0 = 0x240,
		.ctl1 = 0x244,
		.ctl2 = 0x248,
		.ctl3 = 0x2FC,
		.sdev = 0x24C,
		.data0 = 0x250,
		.data1 = 0x254,
		.data2 = 0x2E0,
		.data3 = 0x2E4,
		.data4 = 0x2E8,
		.data5 = 0x2EC,
		.data6 = 0x2F0,
		.data7 = 0x2F4,
		.data8 = 0x2F8,
		.intren = 0x25C,
		.intren_fld = SIF_Fld(1,0),
		.intrst = 0x260,
		.intrst_fld = SIF_Fld(1,0),
		.intrcl = 0x264,
		.intrcl_fld = SIF_Fld(1,0),
		.hwrst = 0x000,
		.hwrst_fld = SIF_Fld(1,28),
		}
	},
	{        
    	.strentch = 1,
    	.devmod = 0,
    	.base =  IO_SIF_BASE,
    	{// i2c1
		.ctl0 = 0x270,
		.ctl1 = 0x274,
		.ctl2 = 0x278,
		.ctl3 = 0x2A8,
		.sdev = 0x27C,
		.data0 = 0x280,
		.data1 = 0x284,
		.data2 = 0x288,
		.data3 = 0x28C,
		.data4 = 0x290,
		.data5 = 0x294,
		.data6 = 0x298,
		.data7 = 0x29C,
		.data8 = 0x2A4,
		.intren = 0x25C,
		.intren_fld = SIF_Fld(1,1),
		.intrst = 0x260,
		.intrst_fld = SIF_Fld(1,1),
		.intrcl = 0x264,
		.intrcl_fld = SIF_Fld(1,1),
		.hwrst = 0x000,
		.hwrst_fld = SIF_Fld(1,27),
		}
	},
#if defined(CC_MT5895)	
	{	.strentch = 1,
#else
	{	.strentch = 0,
#endif
		.devmod = 0,
		.base =  IO_SIF_BASE,
		{// i2c2
		.ctl0 = 0x200,
		.ctl1 = 0x204,
		.ctl2 = 0x208,
		.ctl3 = 0x238,
		.sdev = 0x20C,
		.data0 = 0x210,
		.data1 = 0x214,
		.data2 = 0x218,
		.data3 = 0x21C,
		.data4 = 0x220,
		.data5 = 0x224,
		.data6 = 0x228,
		.data7 = 0x22C,
		.data8 = 0x234,
		.intren = 0x25C,
		.intren_fld = SIF_Fld(1,2),
		.intrst = 0x260,
		.intrst_fld = SIF_Fld(1,2),
		.intrcl = 0x264,
		.intrcl_fld = SIF_Fld(1,2),
		.hwrst = 0x000,
		.hwrst_fld = SIF_Fld(1,26),
		}
	},
#if defined(CC_MT5895)	
	{	.strentch = 1,
		.devmod = 0,
		.base =  PDWNC_IO_BASE,
		{// i2c3
		.ctl0 = 0x780,
		.ctl1 = 0x784,
		.ctl2 = 0x790,
		.ctl3 = 0x794,
		.sdev = 0x7B4,
		.data0 = 0x788,
		.data1 = 0x78C,
		.data2 = 0x798,
		.data3 = 0x79C,
		.data4 = 0x7A0,
		.data5 = 0x7A4,
		.data6 = 0x7A8,
		.data7 = 0x7AC,
		.data8 = 0x7B0,
		.intren = 0x44,
		.intren_fld = SIF_Fld(1,8),
		.intrst = 0x40,
		.intrst_fld = SIF_Fld(1,8),
		.intrcl = 0x48,
		.intrcl_fld = SIF_Fld(1,8),
		.hwrst = 0xC24, // no i2c hardware reset in pdwnc, use sdev last bit.
		.hwrst_fld = SIF_Fld(1,31),
		}
	},
#else
	{	.strentch = 1,
		.devmod = 0,
		.base =  PDWNC_IO_BASE,
		{// i2c3
		.ctl0 = 0x1C0,
		.ctl1 = 0x1C4,
		.ctl2 = 0xC00,
		.ctl3 = 0xC04,
		.sdev = 0xC24,
		.data0 = 0x1C8,
		.data1 = 0x1CC,
		.data2 = 0xC08,
		.data3 = 0xC0C,
		.data4 = 0xC10,
		.data5 = 0xC14,
		.data6 = 0xC18,
		.data7 = 0xC1C,
		.data8 = 0xC20,
		.intren = 0x44,
		.intren_fld = SIF_Fld(1,8),
		.intrst = 0x40,
		.intrst_fld = SIF_Fld(1,8),
		.intrcl = 0x48,
		.intrcl_fld = SIF_Fld(1,8),
		.hwrst = 0xC24, // no i2c hardware reset in pdwnc, use sdev last bit.
		.hwrst_fld = SIF_Fld(1,31),
		}
	},
#endif	
	{	.strentch = 1,
		.devmod = 0,
		.base =  PDWNC_IO_BASE,
		{// i2c4
		.ctl0 = 0x160,
		.ctl1 = 0x164,
		.ctl2 = 0xC28,
		.ctl3 = 0xC2C,
		.sdev = 0xC4C,
		.data0 = 0x168,
		.data1 = 0x16C,
		.data2 = 0xC30,
		.data3 = 0xC34,
		.data4 = 0xC38,
		.data5 = 0xC3C,
		.data6 = 0xC40,
		.data7 = 0xC44,
		.data8 = 0xC48,
		.intren = 0x44,
		.intren_fld = SIF_Fld(1,20),
		.intrst = 0x40,
		.intrst_fld = SIF_Fld(1,20),
		.intrcl = 0x48,
		.intrcl_fld = SIF_Fld(1,20),
		.hwrst = 0xC24, // no i2c hardware reset in pdwnc, use sdev last bit.
		.hwrst_fld = SIF_Fld(1,31),
		}
	},
	{	.strentch = 1,
		.devmod = 0,
		.base =  IO_SIF_SM3_BASE,
		{// i2c5  used in capy
    	.ctl0 = 0x000,
    	.ctl1 = 0x004,
    	.ctl2 = 0x008,
    	.ctl3 = 0x00C,
    	.sdev = 0x010,
    	.data0 = 0x014,
    	.data1 = 0x018,
    	.data2 = 0x01C,
    	.data3 = 0x020,
    	.data4 = 0x024,
    	.data5 = 0x028,
    	.data6 = 0x02C,
    	.data7 = 0x030,
    	.data8 = 0x034,
    	.intren = 0x100,
    	.intren_fld = SIF_Fld(1,0),
    	.intrst = 0x104,
    	.intrst_fld = SIF_Fld(1,0),
    	.intrcl = 0x108,
    	.intrcl_fld = SIF_Fld(1,0),
    	.hwrst = 0x010, // no i2c hardware reset in pdwnc, use sdev highest bit.
    	.hwrst_fld = SIF_Fld(1,31),
    	}
	}
};

//---------------------------------------------------------------------------
// Static functions
//---------------------------------------------------------------------------

//---------------------------------------------------------------------------
// Inter-file functions
//---------------------------------------------------------------------------
INT32 SIF_HwInit(UINT8 u1Channel)
{
	I2CMAST_T TmpMast = arMasters[u1Channel];
	I2CREG_T TmpReg = TmpMast.I2cReg;
	vIO32WriteFldAlign(TmpMast.base+TmpReg.intrcl,1,TmpReg.intrcl_fld);
	vIO32WriteFldAlign(TmpMast.base+TmpReg.intren,0,TmpReg.intren_fld);

	vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl0,0,I2C_CTL0_ODRAIN);
	if(TmpMast.strentch == 0)
		vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl0,0x0,I2C_CTL0_SCL_STRETCH);
	else
		vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl0,0x1,I2C_CTL0_SCL_STRETCH);
	vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl0,0x1,I2C_CTL0_DEG_EN);
	vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl0,0x3,I2C_CTL0_DEG_CNT);
	vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl0,0x1,I2C_CTL0_EN);

	//vIO32WriteFldAlign(TmpMast.base+TmpReg.intrcl,1,TmpReg.intrcl_fld);
	//vIO32WriteFldAlign(TmpMast.base+TmpReg.intren,1,TmpReg.intren_fld);
	return 0;
}
#ifdef CC_CLI

INT32 SIF_Master_Stop(UINT8 u1Channel)
{
	I2CMAST_T TmpMast = arMasters[u1Channel];
	I2CREG_T TmpReg = TmpMast.I2cReg;

	vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl0,0x0,I2C_CTL0_EN);	
	vIO32WriteFldAlign(TmpMast.base+TmpReg.intren,0,TmpReg.intren_fld);
	vIO32WriteFldAlign(TmpMast.base+TmpReg.intrcl,1,TmpReg.intrcl_fld);


}
#endif
static INT32 mtk_i2c_reset(UINT8 u1Channel)
{
	I2CMAST_T TmpMast = arMasters[u1Channel];
	I2CREG_T TmpReg = TmpMast.I2cReg;

	vIO32WriteFldAlign(TmpMast.base+TmpReg.intren,0,TmpReg.intren_fld);
	vIO32WriteFldAlign(TmpMast.base+TmpReg.intrcl,1,TmpReg.intrcl_fld);
	vIO32WriteFldAlign(TmpMast.base+TmpReg.hwrst,1,TmpReg.hwrst_fld);
	vIO32WriteFldAlign(TmpMast.base+TmpReg.hwrst,0,TmpReg.hwrst_fld);
	vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl0,0x1,I2C_CTL0_EN);

	SIF_HwInit(u1Channel);

	return 0;
}

static INT32 SIF_WaitHwDone(UINT8 u1Channel)
{
	UINT32 timer;
	volatile UINT32 u4Tri;
	I2CMAST_T TmpMast = arMasters[u1Channel];
	I2CREG_T TmpReg = TmpMast.I2cReg;

	vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl1, 0x1, I2C_CTL1_TRI);

    u4Tri = timer = 1;
    while (u4Tri && (timer < 0xffff))
    {
    	timer ++;
        u4Tri = IO32ReadFldAlign(TmpMast.base+TmpReg.ctl1, I2C_CTL1_TRI);    
    }
//should not wait forever if sif error!!!!
	if(timer == 0xffff)
	{
		LOG(0,"mapped channel %d timeout\n",u1Channel);
		LOG(0,"mapped channel %d ctl0(%u)=0x%x\n",u1Channel,TmpMast.base+TmpReg.ctl0,u4IO32Read4B(TmpMast.base+TmpReg.ctl0));
		LOG(0,"mapped channel %d ctl1(%u)=0x%x\n",u1Channel,TmpMast.base+TmpReg.ctl1,u4IO32Read4B(TmpMast.base+TmpReg.ctl1));
		LOG(0,"mapped channel %d ctl2(%u)=0x%x\n",u1Channel,TmpMast.base+TmpReg.ctl2,u4IO32Read4B(TmpMast.base+TmpReg.ctl2));
		LOG(0,"mapped channel %d ctl3(%u)=0x%x\n",u1Channel,TmpMast.base+TmpReg.ctl3,u4IO32Read4B(TmpMast.base+TmpReg.ctl3));
		LOG(0,"mapped channel %d sdev(%u)=0x%x\n",u1Channel,TmpMast.base+TmpReg.sdev,u4IO32Read4B(TmpMast.base+TmpReg.sdev));
		LOG(0,"mapped channel %d intren(%u)=0x%x\n",u1Channel,TmpMast.base+TmpReg.intren,u4IO32Read4B(TmpMast.base+TmpReg.intren));
		LOG(0,"mapped channel %d intrst(%u)=0x%x\n",u1Channel,TmpMast.base+TmpReg.intrst,u4IO32Read4B(TmpMast.base+TmpReg.intrst));
		LOG(0,"mapped channel %d intrcl(%u)=0x%x\n",u1Channel,TmpMast.base+TmpReg.intrcl,u4IO32Read4B(TmpMast.base+TmpReg.intrcl));
		mtk_i2c_reset(u1Channel);
		return I2C_ERROR_TIMEOUT;
	}
	return I2C_OK;
}
// check i2c bus states
static INT32 SIF_CheckBus(UINT8 u1Channel)
{
	UINT8 scl=0,sda=0;
	I2CMAST_T TmpMast = arMasters[u1Channel];
	I2CREG_T TmpReg = TmpMast.I2cReg;

	scl = (UINT8)(IO32ReadFldAlign(TmpMast.base+TmpReg.ctl0,I2C_CTL0_SCL_STATE));
	sda = (UINT8)(IO32ReadFldAlign(TmpMast.base+TmpReg.ctl0,I2C_CTL0_SDA_STATE));
	return (scl << 1) | sda ;
}
// put start bit
#if 0
static INT32 SIF_PutStart(UINT8 u1Channel)
{
	UINT32 ret=0;
	I2CMAST_T TmpMast = arMasters[u1Channel];
	I2CREG_T TmpReg = TmpMast.I2cReg;

	vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl3,0x00,I2C_CTL3_ACT_MODE);
	vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl1,MODE_START,I2C_CTL1_MODE);
	ret = SIF_WaitHwDone(u1Channel);
	return ret;
}
#endif

// put stop bit

static INT32 SIF_PutStop(UINT8 u1Channel)
{
	UINT32 ret=0;
	I2CMAST_T TmpMast = arMasters[u1Channel];
	I2CREG_T TmpReg = TmpMast.I2cReg;
	vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl3,0x0,I2C_CTL3_ACT_MODE);
	vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl1,MODE_STOP,I2C_CTL1_MODE);
	ret = SIF_WaitHwDone(u1Channel);
	return ret;
}

// check ack
static INT32 SIF_CheckAck(UINT32 ackh, UINT32 ackl)
{
	INT32 i=0;
	UINT32 cnt=0,ack = (ackh << 16) | ackl;
	
	if(ackh >= 0xffff)
	{
		if(ackl != 0xffff)
		{
			cnt = 0;
		}
		else
		{
			for(i=4;i>=0;i--)
			{
				if(ackh == ((1<<(i+16)) - 1))
				{
					cnt = i + 32;
				}
			}
		}
	}
	else
	{
		
		for(i=31;i>0;i--)
		{
			if(ack == ((1<<i) -1))
			{
				cnt = i;
			}
		}
	}
	
	return cnt;
}
// put bytes

static INT32 SIF_PutBytes(UINT8 u1Channel, UINT8 *buf, UINT32 len, UINT8 mode)
{
	UINT8 *pbuf=buf;
	INT32 i=0,j=0;
	UINT8 sack;
	UINT32 ackh=0,ackl=0;
	INT32 ack_cnt=0,cnt=0,ret=0;
	INT32 total_loop=0,split_bytes=0;
	UINT32 d0,d1,d2,d3,d4,d5,d6,d7,d8;
	I2CMAST_T TmpMast = arMasters[u1Channel];
	I2CREG_T TmpReg = TmpMast.I2cReg;

	total_loop = (len + 35) / 36;
	for(i=0;i<total_loop;i++)
	{
		if(i == (total_loop - 1))
		{

			if(IF_STOP(mode))
			{
				vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl3,0x40,I2C_CTL3_ACT_MODE);
				vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl1, 0, I2C_CTL1_MODE);
			}
			else
			{
				vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl3,0x00,I2C_CTL3_ACT_MODE);
				vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl1, MODE_WRITE, I2C_CTL1_MODE);
			}
			split_bytes = len % 36;
		}
		else
		{
			if(i == 0)
			{

				if(IF_START(mode))
				{
					vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl3,0x21,I2C_CTL3_ACT_MODE);
					vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl1, 0, I2C_CTL1_MODE);
				}				
				else
				{
					vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl3,0x00,I2C_CTL3_ACT_MODE);
					vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl1, MODE_WRITE, I2C_CTL1_MODE);
				}
			}
			else
			{
				vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl3,0x00,I2C_CTL3_ACT_MODE);
				vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl1, MODE_WRITE, I2C_CTL1_MODE);
			}
			split_bytes = 36;
		}
		if(split_bytes == 0)
		{
			split_bytes = 36;
		}
		if(total_loop == 1)
		{
			
			if(!IF_START(mode) && !IF_STOP(mode))
			{
				vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl3,0x00,I2C_CTL3_ACT_MODE);
				vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl1, MODE_WRITE, I2C_CTL1_MODE);
			}
			else if(IF_START(mode) && !IF_STOP(mode))
			{
				vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl3,0x21,I2C_CTL3_ACT_MODE);
				vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl1, 0, I2C_CTL1_MODE);
			}
			else if(!IF_START(mode) && IF_STOP(mode))
			{
				vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl3,0x40,I2C_CTL3_ACT_MODE);
				vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl1, 0, I2C_CTL1_MODE);
			}
			else //(IF_START(mode) && IF_STOP(mode))
			{
				vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl3,0x11,I2C_CTL3_ACT_MODE);
				vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl1, 0, I2C_CTL1_MODE);
			}
		}

		d0 = 0;
		d1 = 0;
		d2 = 0;
		d3 = 0;
		d4 = 0;
		d5 = 0;
		d6 = 0;
		d7 = 0;
		d8 = 0;

		for(j=0; j< ((split_bytes > 4)? 4: split_bytes); j++)
		{
			d0 |= ((pbuf[j] << (8*j)) & (0xff << (8*j)));
		}

		if(split_bytes > 4)
		{
			for(j=0; j< ((split_bytes > 8)? 4: split_bytes-4); j++)
			{
				d1 |= ((pbuf[4+j] << (8*j)) & (0xff << (8*j)));
			}
		}

		if(split_bytes > 8)
		{
			for(j=0; j< ((split_bytes > 12)? 4: split_bytes-8); j++)
			{
				d2 |= ((pbuf[8+j] << (8*j)) & (0xff << (8*j)));
			}
		}

		if(split_bytes > 12)
		{
			for(j=0; j< ((split_bytes > 16)? 4: split_bytes-12); j++)
			{
				d3 |= ((pbuf[12+j] << (8*j)) & (0xff << (8*j)));
			}
		}

		if(split_bytes > 16)
		{
			for(j=0; j< ((split_bytes > 20)? 4: split_bytes-16); j++)
			{
				d4 |= ((pbuf[16+j] << (8*j)) & (0xff << (8*j)));
			}
		}

		if(split_bytes > 20)
		{
			for(j=0; j< ((split_bytes > 24)? 4: split_bytes-20); j++)
			{
				d5 |= ((pbuf[20+j] << (8*j)) & (0xff << (8*j)));
			}
		}

		if(split_bytes > 24)
		{
			for(j=0; j< ((split_bytes > 28)? 4: split_bytes-24); j++)
			{
				d6 |= ((pbuf[24+j] << (8*j)) & (0xff << (8*j)));
			}
		}

		if(split_bytes > 28)
		{
			for(j=0; j< ((split_bytes > 32)? 4: split_bytes-28); j++)
			{
				d7 |= ((pbuf[28+j] << (8*j)) & (0xff << (8*j)));
			}
		}

		if(split_bytes > 32)
		{
			for(j=0; j< (split_bytes-32); j++)
			{
				d8 |= ((pbuf[32+j] << (8*j)) & (0xff << (8*j)));
			}
		}
		pbuf += split_bytes;
		vIO32Write4B(TmpMast.base+TmpReg.data0,d0);
		vIO32Write4B(TmpMast.base+TmpReg.data1,d1);
		vIO32Write4B(TmpMast.base+TmpReg.data2,d2);
		vIO32Write4B(TmpMast.base+TmpReg.data3,d3);
		vIO32Write4B(TmpMast.base+TmpReg.data4,d4);
		vIO32Write4B(TmpMast.base+TmpReg.data5,d5);
		vIO32Write4B(TmpMast.base+TmpReg.data6,d6);
		vIO32Write4B(TmpMast.base+TmpReg.data7,d7);
		vIO32Write4B(TmpMast.base+TmpReg.data8,d8);

		vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl1, split_bytes -1, I2C_CTL1_PGLEN);
		ret = SIF_WaitHwDone(u1Channel);
		if(ret != I2C_OK)
		{
			return ret;
		}

		if(i==0 && IF_START(mode))
		{
			sack = IO32ReadFldAlign(TmpMast.base+TmpReg.ctl3,I2C_CTL3_SACK);
			if(sack == 0)
			{
				return I2C_ERROR_NODEV;
			}
		}
		ackl = IO32ReadFldAlign(TmpMast.base+TmpReg.ctl1,I2C_CTL1_ACK0);
		ackh = IO32ReadFldAlign(TmpMast.base+TmpReg.ctl3,I2C_CTL3_ACK1);
		
		ack_cnt = SIF_CheckAck(ackh,ackl);
		
		if(ack_cnt!=split_bytes)
		{
			LOG(0,"abnormal device ack:send %d, ack %d (0x%x 0x%x).\n",split_bytes,ack_cnt,ackh,ackl);
			ret = SIF_PutStop(u1Channel);
			if(ret != I2C_OK)
			{
				LOG(0,"write:put stop bit error\n");
			}
			return I2C_ERROR_DATA;
		}
		cnt += ack_cnt;
	}
	if(cnt != len)
	{
		return I2C_ERROR_DATA;
	}
	return cnt;
}
// get bytes

static INT32 SIF_GetBytes(UINT8 u1Channel, UINT8 *buf, UINT32 len, UINT8 mode)
{
	UINT8 *pbuf=buf;
	UINT32 total_loop=0,split_bytes=0;
	INT32 cnt=0,ret=0,i=0,j=0;
	UINT8 sack;
	UINT32 d0,d1,d2,d3,d4,d5,d6,d7,d8;
	I2CMAST_T TmpMast = arMasters[u1Channel];
	I2CREG_T TmpReg = TmpMast.I2cReg;

	total_loop = (len + 35) / 36;
	for(i=0;i<total_loop;i++)
	{
		if(i == (total_loop -1))
		{

			if(IF_STOP(mode))
			{
				vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl3,0x08,I2C_CTL3_ACT_MODE);
				vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl1, 0, I2C_CTL1_MODE);
			}			
			else
			{
				vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl3,0x00,I2C_CTL3_ACT_MODE);
				vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl1, MODE_READ, I2C_CTL1_MODE);
			}
			split_bytes = len % 36;
		}
		else
		{
			if(i == 0)
			{

				if(IF_START(mode))
				{
					vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl3,0x05,I2C_CTL3_ACT_MODE);
					vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl1, 0, I2C_CTL1_MODE);
				}				
				else
				{
					vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl3,0x00,I2C_CTL3_ACT_MODE);
					vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl1, MODE_READ, I2C_CTL1_MODE);
				}
			}
			else
			{
				vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl3,0x00,I2C_CTL3_ACT_MODE);
				vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl1, MODE_READ, I2C_CTL1_MODE);
			}
			split_bytes = 36;
		}
		if(split_bytes == 0)
		{
			split_bytes = 36;
		}

		if(total_loop == 1)
		{
			if(!IF_START(mode) && !IF_STOP(mode))
			{
				vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl3,0x00,I2C_CTL3_ACT_MODE);
				vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl1, MODE_READ, I2C_CTL1_MODE);
			}else if(IF_START(mode) && !IF_STOP(mode))
			{
				vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl3,0x05,I2C_CTL3_ACT_MODE);
				vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl1, 0, I2C_CTL1_MODE);
			}else if(!IF_START(mode) && IF_STOP(mode))
			{
				vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl3,0x08,I2C_CTL3_ACT_MODE);
				vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl1, 0, I2C_CTL1_MODE);
			}
			else //(IF_START(mode) && IF_STOP(mode))
			{
				vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl3,0x03,I2C_CTL3_ACT_MODE);
				vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl1, 0, I2C_CTL1_MODE);
			}
		}

		vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl1, split_bytes-1, I2C_CTL1_PGLEN);
		ret = SIF_WaitHwDone(u1Channel);
		if(ret != I2C_OK)
		{
			return ret;
		}
          
		if(i==0 && IF_START(mode))
		{
			sack = IO32ReadFldAlign(TmpMast.base+TmpReg.ctl3,I2C_CTL3_SACK);
			if(sack == 0)
			{
				return I2C_ERROR_NODEV;
			}
		}

		d0 = u4IO32Read4B(TmpMast.base+TmpReg.data0);
		d1 = u4IO32Read4B(TmpMast.base+TmpReg.data1);
		d2 = u4IO32Read4B(TmpMast.base+TmpReg.data2);
		d3 = u4IO32Read4B(TmpMast.base+TmpReg.data3);
		d4 = u4IO32Read4B(TmpMast.base+TmpReg.data4);
		d5 = u4IO32Read4B(TmpMast.base+TmpReg.data5);
		d6 = u4IO32Read4B(TmpMast.base+TmpReg.data6);
		d7 = u4IO32Read4B(TmpMast.base+TmpReg.data7);
		d8 = u4IO32Read4B(TmpMast.base+TmpReg.data8);

		for(j=0;j<((split_bytes > 4) ? 4 : split_bytes); j++)
		{
			*pbuf = (UINT8) (d0 & 0xff);
			d0 >>=8;
			pbuf++;
			cnt++;
		}

		if(split_bytes > 4)
		{
			for(j=0;j<((split_bytes > 8) ? 4 : split_bytes-4); j++)
			{
				*pbuf = (UINT8) (d1 & 0xff);
				d1 >>=8;
				pbuf++;
				cnt++;
			}
		}

		if(split_bytes > 8)
		{
			for(j=0;j<((split_bytes > 12) ? 4 : split_bytes-8); j++)
			{
				*pbuf = (UINT8) (d2 & 0xff);
				d2 >>=8;
				pbuf++;
				cnt++;
			}
		}

		if(split_bytes > 12)
		{
			for(j=0;j<((split_bytes > 16) ? 4 : split_bytes-12); j++)
			{
				*pbuf = (UINT8) (d3 & 0xff);
				d3 >>=8;
				pbuf++;
				cnt++;
			}
		}

		if(split_bytes > 16)
		{
			for(j=0;j<((split_bytes > 20) ? 4 : split_bytes-16); j++)
			{
				*pbuf = (UINT8) (d4 & 0xff);
				d4 >>=8;
				pbuf++;
				cnt++;
			}
		}

		if(split_bytes > 20)
		{
			for(j=0;j<((split_bytes > 24) ? 4 : split_bytes-20); j++)
			{
				*pbuf = (UINT8) (d5 & 0xff);
				d5 >>=8;
				pbuf++;
				cnt++;
			}
		}

		if(split_bytes > 24)
		{
			for(j=0;j<((split_bytes > 28) ? 4 : split_bytes - 24); j++)
			{
				*pbuf = (UINT8) (d6 & 0xff);
				d6 >>=8;
				pbuf++;
				cnt++;
			}
		}

		if(split_bytes > 28)
		{
			for(j=0;j<((split_bytes > 32) ? 4 : split_bytes - 28); j++)
			{
				*pbuf = (UINT8) (d7 & 0xff);
				d7 >>=8;
				pbuf++;
				cnt++;
			}
		}

		if(split_bytes > 32)
		{
			for(j=0;j<(split_bytes - 32); j++)
			{
				*pbuf = (UINT8) (d8 & 0xff);
				d8 >>=8;
				pbuf++;
				cnt++;
			}
		}
	}
	if(cnt != len)
	{
		return I2C_ERROR_DATA;
	}
	return cnt;
}

// do action
static INT32 SIF_DoAction(UINT8 u1Channel, UINT8 addr, UINT32 sub_addr,
		UINT8 sub_addr_num, UINT8 isread, UINT8 *buf, UINT32 len)
{
	I2CMAST_T TmpMast = arMasters[u1Channel];
	I2CREG_T TmpReg = TmpMast.I2cReg;
	INT32 ret=0,cnt=0;
	UINT8 subaddr[4]={0};
	
	// check bus is busy
	if(SIF_CheckBus(u1Channel)!=0x3)
	{
		ret = SIF_PutStop(u1Channel);
		if(ret != I2C_OK)
		{
			LOG(0,"checkbus:put stop bit error\n");
		}
		if(SIF_CheckBus(u1Channel)!=0x3)
		{
			//LOG(0,"checkbus:bus is too busy to action\n");
			return I2C_ERROR_BUS_BUSY;
		}
	}

	if(sub_addr_num) // sub address
	{
		if(sub_addr_num == 3)
		{
			subaddr[0] = (UINT8)((sub_addr & 0xff0000) >> 16);
			subaddr[1] = (UINT8)((sub_addr & 0x00ff00) >> 8);
			subaddr[2] = (UINT8)((sub_addr & 0x0000ff));
		}
		else if(sub_addr_num == 2)
		{
			subaddr[0] = (UINT8)((sub_addr & 0x00ff00) >> 8);
			subaddr[1] = (UINT8)((sub_addr & 0x0000ff));
		}
		else if(sub_addr_num == 1)
		{
			subaddr[0] = (UINT8)((sub_addr & 0x0000ff));
		}
		else if(sub_addr_num == 4)
		{
			subaddr[0] = (UINT8) ((sub_addr & 0xFF000000) >> 24);
			subaddr[1] = (UINT8) ((sub_addr & 0x00FF0000) >> 16);
			subaddr[2] = (UINT8) ((sub_addr & 0x0000FF00) >> 8);
			subaddr[3] = (UINT8) ((sub_addr & 0x000000FF));
		}
		else
		{
			LOG(0,"subaddr error %d\n",sub_addr_num);
			return I2C_ERROR_SUBADDR;
		}
		vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl1,0,I2C_CTL1_MODE);
		vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl3,0x21,I2C_CTL3_ACT_MODE);
		vIO32WriteFldAlign(TmpMast.base+TmpReg.sdev,addr,I2C_SDEV_ADDR);
		cnt = SIF_PutBytes(u1Channel,(UINT8*)(&subaddr),sub_addr_num,NEED_START);
		if(cnt != (UINT32)sub_addr_num)
		{
			ret = SIF_PutStop(u1Channel);
			if(ret != I2C_OK)
			{
				LOG(0,"subaddr:put stop bit error\n");
			}
			if(cnt == I2C_ERROR_NODEV)
			{
				return I2C_ERROR_NODEV;
			}
			else
			{
				return I2C_ERROR_SUBADDR;
			}
		}

		if(isread)
		{
			vIO32WriteFldAlign(TmpMast.base+TmpReg.sdev,addr|1,I2C_SDEV_ADDR);
			cnt = SIF_GetBytes(u1Channel,buf,len,NEED_START_STOP);
		}
		else
		{
			vIO32WriteFldAlign(TmpMast.base+TmpReg.sdev,addr,I2C_SDEV_ADDR);
			cnt = SIF_PutBytes(u1Channel,buf,len,NEED_STOP);
		}
	}
	else
	{
		vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl1,0,I2C_CTL1_MODE);
		if(isread)
		{
			vIO32WriteFldAlign(TmpMast.base+TmpReg.sdev,addr|1,I2C_SDEV_ADDR);
			cnt = SIF_GetBytes(u1Channel,buf,len,NEED_START_STOP);
		}
		else
		{
			vIO32WriteFldAlign(TmpMast.base+TmpReg.sdev,addr,I2C_SDEV_ADDR);
			cnt = SIF_PutBytes(u1Channel,buf,len,NEED_START_STOP);
		}
	}
	
	vIO32WriteFldAlign(TmpMast.base+TmpReg.ctl3,0x00,I2C_CTL3_ACT_MODE);

	if(SIF_CheckBus(u1Channel)!=0x3)
	{
		ret = SIF_PutStop(u1Channel);
		if(ret != I2C_OK)
		{
			LOG(0,"%s:put stop bit error\n",__FUNCTION__);
		}
		if(SIF_CheckBus(u1Channel)!=0x3)
		{
			LOG(0,"%s:bus is still busy\n",__FUNCTION__);
			mtk_i2c_reset(u1Channel);
		}
	}
	
	return cnt;
}


UINT16 SIF_RW(UINT16 clkdiv, UINT8 u1Channel, UINT8 addr, UINT32 sub_addr,
		UINT8 sub_addr_num, UINT8 isread, UINT8 *buf, UINT32 len)
{
	I2CMAST_T TmpMast = arMasters[u1Channel];
	I2CREG_T TmpReg = TmpMast.I2cReg;
	UINT32 base = TmpMast.base;
	UINT32 riseval;
	INT32  i4Ret;
	#ifdef CC_FIXED_SIF_CLK_DIV
    UINT32 u4FixClkDiv;
    u4FixClkDiv = DRVCUST_InitGet(eSifcfgFixClkDiv);
	clkdiv = u4FixClkDiv;
	#endif
	// set clkdiv
	
	ASSERT(u1Channel >= 0);
	if(clkdiv < 0x20)
	{
		//LOG(0,"xfer: clkdiv < 0x20 , limit it to 0x20\n");
		clkdiv = 0x20;
		if(TmpMast.strentch != 0)
		{
			riseval = 0x20 * 40 / 81;
		}
		else
		{
			riseval = 0x20 * 3 / 5;
		}
		vIO32WriteFldAlign(base+TmpReg.ctl2,riseval,I2C_CTL2_SCL_RISE_VALUE);
		vIO32WriteFldAlign(base+TmpReg.ctl2,0x1,I2C_CTL2_SCL_MODE);
	}
	else if(clkdiv >= 0x20 && clkdiv < 0x80)
	{
		if(TmpMast.strentch != 0)
		{
			riseval = clkdiv * 40 / 81;
		}
		else
		{
			riseval = clkdiv * 3 / 5;
		}
		//LOG(0,"xfer: clkdiv < 0x80 , use fast mode\n");
		vIO32WriteFldAlign(base+TmpReg.ctl2,riseval,I2C_CTL2_SCL_RISE_VALUE);
		vIO32WriteFldAlign(base+TmpReg.ctl2,0x1,I2C_CTL2_SCL_MODE);
	}
	else
	{
		//LOG(0,"xfer: clkdiv >= 0x80, use standard mode\n");
		vIO32WriteFldAlign(base+TmpReg.ctl2,0,I2C_CTL2_SCL_MODE);
	}
	
	//LOG(0,"%s clkdiv %x addr_num %x,addr %x\n",isread?"read":"write",clkdiv,sub_addr_num,sub_addr);
	
	vIO32WriteFldAlign(base+TmpReg.ctl0,clkdiv,I2C_CTL0_CLK_DIV);

#if 0
	while(num)
	{
		dev_addr=msgs[msg_idx].addr;
		buf2=msgs[msg_idx].buf;
		len=msgs[msg_idx].len;
		isread=(msgs[msg_idx].flags&I2C_M_RD)?1:0;
		//pr_info("action:%s, count=%d\n",isread?"read":"write",len);
		ret=mtk_i2c_do_action(adap,dev_addr,sub_addr,
				sub_addr_num,isread,buf2,len);
		msg_idx++;
		num--;
	}
#endif

	i4Ret = SIF_DoAction(u1Channel, addr, sub_addr, sub_addr_num, isread, buf, len);
	if (i4Ret < 0)
	{	
		LOG(0,"error value %d\n",i4Ret);
		i4Ret = 0;
	}
    return i4Ret;

}

