/* 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: 2018/01/01 $
 * $RCSfile: preph_gspi.c,v $
 * $Revision: #2 $
 *
 *---------------------------------------------------------------------------*/

/** @file preph_spi.c
 *  preph_spi.c The PDWNC (Power Down Controller) GSPI functions
 */

#include "x_pinmux.h"
#include "x_bim.h"
#include "x_assert.h"
#include "x_os.h"
#include "x_ckgen.h"
#include "x_timer.h"
#include "x_hal_arm.h"
#include "x_gpio.h"
#include "drvcust_if.h"
#include "spi_hw_comm.h"
#include "spi_debug.h"
#include "spi_comm_if.h"
#include "x_timer.h"
#include "x_pdwnc.h"


#ifndef CC_MTK_LOADER
//#define PREPH_SPI_INT_MOD 
#define PREPH_SPI_DMA_MOD  
//coreherent fix buf is 4k,so max packet size should not more than 4k
//single buf do not has this limitation
//#define SPI_USE_SEMA
//enable watchdog0 to test 
#endif

#ifndef CC_MTK_LOADER
#include <linux/soc/mediatek/x_typedef.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/time.h>
#include <linux/preempt.h>
#ifdef PREPH_SPI_DMA_MOD
#include <linux/dma-mapping.h>
#include <asm/cacheflush.h>
#endif
#if defined(CC_SUPPORT_STR_CORE_OFF)
#include <asm/atomic.h>
#endif
#endif

//we can not use int but we can use dma mode in loader
//dma must in the first emi region for capy 31x31 (1G-4G)
//dma mask just can set 0-4G!!!!

#ifdef PREPH_SPI_DMA_MOD
#define SPI_CI_COREHERENT_DMABUF
#endif
#define MTK_SPI_PAUSE_INT_STATUS 0x2
#define MTK_SPI_IDLE 0
#define MTK_SPI_PAUSED 1
#define SPI_LOOP_TIMEOUT			(0xffff)
#define PREPH_SPI_MAX_FIFO_SIZE 	(32)
#define PREPH_SPI_DMA_PACKET_SIZE 	(4096)
#define PREPH_SPI_IO_MAX_CLK  		(54)
#define PREPH_SPI_MAX_CLK  			(216)

//capy io max suport 50Mhz , so we set the max spi clk is 54Mhz
#ifdef  SPI_CI_COREHERENT_DMABUF
#define FIX_CI_DMA_BUF   PREPH_SPI_DMA_PACKET_SIZE
#endif


#ifndef CC_MTK_LOADER
static HANDLE_T SpiIsrSema      = NULL_HANDLE;
#define SPI_ISR_SEMA_TIMEOUT    (2000)
static BOOL _fgPrephSpiInit = FALSE;
#endif

#ifndef CC_MTK_LOADER
#ifdef SPI_USE_SEMA
static HANDLE_T SpiUserSema      = NULL_HANDLE;
#endif
#define SPI_USER_SEMA_TIMEOUT    (1000)
//fifo mode max packet is 32byte 
//if we set packet align with 32byte,there is no need to use this define 
//if we set packet align little than 32byte 
//this define is need and some thing need to do in this define with fifo mode
#endif

#ifdef SPI_CI_COREHERENT_DMABUF
void SpiGetCorherentDMABuf(void);
#endif

static void SpiDump(void)
{	
	LOG(0,"SPI_CMD:0x%x\n",u4IO32Read4B(SPI_CMD_REG));
	LOG(0,"SPI_CFG0:0x%x\n",u4IO32Read4B(SPI_CFG0_REG));
	LOG(0,"SPI_CFG1:0x%x\n",u4IO32Read4B(SPI_CFG1_REG));	
	LOG(0,"SPI_DMA_TX:0x%x\n",u4IO32Read4B(SPI_TX_SRC_REG));	
	LOG(0,"SPI_DMA_RX:0x%x\n",u4IO32Read4B(SPI_RX_DST_REG));	
	LOG(0,"SPI_STATUS1:0x%x\n",u4IO32Read4B(SPI_STATUS1_REG));	
	LOG(0,"SPI_STATUS0:0x%x\n",u4IO32Read4B(SPI_STATUS0_REG));
}
static void SpiReset(void)
{
    vIO32WriteFldAlign(SPI_CMD_REG, 0x1, FLD_SPI_RST);	
    vIO32WriteFldAlign(SPI_CMD_REG, 0x0, FLD_SPI_RST);
	SpiClkInit(PREPH_SPI_IO_MAX_CLK);
}
#if defined(CC_SUPPORT_STR_CORE_OFF)
static atomic_t SpiSuspended = ATOMIC_INIT(0);
static BOOL SpiIssuspended(void)
{
	return (atomic_read(&SpiSuspended) == 1);
}
static void SpiSetsuspended(void)
{	
	atomic_set(&SpiSuspended,1);
}
static void SpiClrsuspended(void)
{
	atomic_set(&SpiSuspended,0);
}

#endif

#ifndef CC_MTK_LOADER
static void SpiIsrCreateSema(void)
{
	VERIFY(x_sema_create(&SpiIsrSema, X_SEMA_TYPE_BINARY, X_SEMA_STATE_LOCK) == OSR_OK);
}

//static INT32 SpiIsrReinnitLock(void)
//{
//	return x_sema_lock(SpiIsrSema, X_SEMA_OPTION_NOWAIT);
//}

static INT32 SpiIsrUnlock(void)
{
    return x_sema_unlock(SpiIsrSema);
}

static INT32 SpiIsrLock(void)
{
    return x_sema_lock_timeout(SpiIsrSema, SPI_ISR_SEMA_TIMEOUT);
}

static void SpiHwIsr(UINT16 u2VctId)
{
    //UINT16 i = 0;
	INT32 i4Ret = 0;
    UINT32 u4Val=0;
    UNUSED(u2VctId);
	
    u4Val |= IO32ReadFldAlign(SPI_STATUS0_REG,FLD_SPI_PAUSE);
    u4Val |= IO32ReadFldAlign(SPI_STATUS0_REG,FLD_SPI_FINISH);
	//these two int status is read clear
    if(IO32ReadFldAlign(SPI_STATUS1_REG,FLD_SPI_BUSY) == 0)	//this bit is low active, 0 means the spi controller is busy now.
    { 	
		LOG(0,"spi busy in isr!!! int status 0x%x\n",u4Val);
		SpiDump();
    }
	i4Ret = SpiIsrUnlock();
	if(i4Ret != OSR_OK)
		LOG(0,"SpiIsrUnlock: i4Ret=%d\n",i4Ret);
	
#ifndef CC_NO_BIM_INTR
	#if defined(CC_MT5895)
	VERIFY(BIM_ClearIrq(VECTOR_PERI_GSPI));
	#elif defined(CC_MT5893)
	VERIFY(BIM_ClearIrq(VECTOR_SPI));
	#endif
#endif
}
static INT32 SpiSetIsr(BOOL fgSet)
{
    static BOOL fgCurSet = FALSE;
    INT32 i4Val;
    void (* pfnOldIsr)(UINT16);

    if (fgSet && (fgSet!=fgCurSet))
    {
    	/* enable irq. */
        fgCurSet = fgSet;
		#if defined(CC_MT5895)
        i4Val = x_reg_isr(VECTOR_PERI_GSPI, SpiHwIsr, &pfnOldIsr);
		#elif defined(CC_MT5893)
        i4Val = x_reg_isr(VECTOR_SPI, SpiHwIsr, &pfnOldIsr);
		#endif
        if (i4Val != OSR_OK)
        {
            return FALSE;
        }
#ifndef CC_NO_BIM_INTR
		#if defined(CC_MT5895)
        VERIFY(BIM_ClearIrq(VECTOR_PERI_GSPI));
		#elif defined(CC_MT5893)
        VERIFY(BIM_ClearIrq(VECTOR_SPI));
		#endif
#endif
    }
    else if (!fgSet && (fgSet!=fgCurSet))
    {
    	/* disable irq. */
        fgCurSet = fgSet;
		#if defined(CC_MT5895)
        i4Val = x_reg_isr(VECTOR_PERI_GSPI, NULL, &pfnOldIsr);
		#elif defined(CC_MT5893)
        i4Val = x_reg_isr(VECTOR_SPI, NULL, &pfnOldIsr);
		#endif
        if (i4Val != OSR_OK)
        {
            return FALSE;
        }
    }
    return TRUE;
}

#endif

#ifdef PREPH_SPI_DMA_MOD

enum data_direction { BIDIRECTIONAL = 0, TO_DEVICE = 1, FROM_DEVICE = 2 };

extern struct platform_device mt53xx_spi_device;

dma_addr_t SpiDmaMap(UINT32* u4Start, UINT32 u4Len, enum data_direction dir)
{
	dma_addr_t dma_addr;
	//dma_addr = dma_map_single(NULL, (void *)u4Start, u4Len, dir);
	
	dma_addr = dma_map_single(&(mt53xx_spi_device.dev), (void *)u4Start, u4Len, (enum dma_data_direction)dir);
	if (dma_mapping_error(&(mt53xx_spi_device.dev), dma_addr))
	{	
		LOG(0,"dma map fail for direction %d\n",dir);
		return 0;
	}
	LOG(2,"dma map addr %llx for dir %d\n",dma_addr,dir);
	if(dir == TO_DEVICE)
	{
		vIO32Write4B(SPI_TX_SRC_REG,dma_addr);
	}
	else if(dir == FROM_DEVICE)
	{
		vIO32Write4B(SPI_RX_DST_REG,dma_addr);
	}
	else if(dir == BIDIRECTIONAL)
	{
		LOG(0,"real need this dma option in spi?\n");		
		return 0;
	}
	else
	{
		LOG(0,"error dma option in spi\n");		
		return 0;
	}
	return dma_addr;
}
void SpiDmaUnmap(dma_addr_t u4Start, UINT32 u4Len, enum data_direction dir)
{
	//dma_unmap_single(NULL, u4Start, u4Len, dir);
	dma_unmap_single(&(mt53xx_spi_device.dev), u4Start, u4Len, (enum dma_data_direction)dir);
}

#endif


static void SpiCSDelayInit(void)
{
	UINT32 u4CsDelay = 0x05;
	UINT32 u4CsHold = 0x08;
	UINT32 u4SapDlay = 0x02;
	//get this value from experiments 
	
	LOG(2,"need cs and samp delay for this preph spi\n");
	
	vIO32WriteFldAlign(SPI_CFG0_REG, u4CsDelay & 0xff, FLD_SPI_CS_SETUP_COUNT);	
	vIO32WriteFldAlign(SPI_CFG0_REG, u4CsHold & 0xff, FLD_SPI_CS_HOLD_COUNT);
	vIO32WriteFldAlign(SPI_CFG1_REG, u4SapDlay & 0x03, FLD_SPI_GET_TICK_DLY);
}

void SpiClkInit(UINT32 u4Clk)
{
	UINT32 u4ClkDiv;
	//u4Clk uint M
	if(!IS_IC_5895())
	{
		LOG(0,"just 5895 support this preph spi\n");	
		return;
	}

	if( u4Clk > PREPH_SPI_IO_MAX_CLK)
	{
		LOG(0,"SPI MAX CLK: %d\n",PREPH_SPI_IO_MAX_CLK);
		u4Clk = PREPH_SPI_IO_MAX_CLK;
	}
	else if(u4Clk == 0)
	{		
		LOG(0,"clk is 0,keep clk as it is\n");
		return;
	}
	//[1:0]
	//0:26    1:72    2:108   3:216
	//max clk 216, max clk div 256,
	//so we just set clk as 216M,use clk div to get the clock we want
	//default duty is 50%
	u4ClkDiv = (PREPH_SPI_MAX_CLK/u4Clk);
	u4ClkDiv >>= 1;
	if(PREPH_SPI_MAX_CLK % u4Clk != 0)
	{
		LOG(2,"real clk is %dMhz\n",PREPH_SPI_MAX_CLK/(u4ClkDiv*2));
	}
	u4ClkDiv -= 1;
#ifdef CC_MT5895
    vIO32WriteFldAlign(CKGEN_REG_PERI_GSPI_CKCFG, 0x3, FLD_PERI_GSPI_CK_SEL);
	vIO32WriteFldAlign(SPI_CFG0_REG, u4ClkDiv & 0xff, FLD_SPI_SCK_LOW_COUNT);	
	vIO32WriteFldAlign(SPI_CFG0_REG, u4ClkDiv & 0xff, FLD_SPI_SCK_HIGH_COUNT);
#endif
	//this delay is realy ineed to cover master sample data error
	SpiCSDelayInit();
	
}

void SpiInit(PREPH_SPI_CONF *SpiInitCon)
{
	UINT32 u4MorL, u4CS, u4Mod, u4EorL;
		
	if(!IS_IC_5895())
	{
		LOG(0,"just 5895 support this preph spi\n");
		return;
	}

	
	u4MorL = SpiInitCon->u4MorL;
	u4CS = SpiInitCon->u4CS;
	u4Mod = SpiInitCon->u4Mod;
	u4EorL = SpiInitCon->u4EorL;
	
	//to make these pin define as common code,add void define if not defined for other ic 
	BSP_PinSet(PIN_SPI_CS,PINMUX_FUNCTION1);
	BSP_PinSet(PIN_SPI_DI,PINMUX_FUNCTION1);
	BSP_PinSet(PIN_SPI_DO,PINMUX_FUNCTION1);
	BSP_PinSet(PIN_SPI_CLK,PINMUX_FUNCTION1);
	SpiReset();
	//pinmux setting 24/25/26/27 as spi function 	
	SpiClkInit(SpiInitCon->u4Clk);
	
	LOG(2,"CPOL:%d CPHA:%d TX:%s RX:%s CS:%s ACTIVE TX:%s RX:%s:\n", 
		((u4Mod&0x02)>>1),u4Mod&0x01,(u4MorL&0x01)?"MSB":"LSB",((u4MorL&0x02)>>1)?"MSB":"LSB",u4CS?"HIGH":"LOW",
		(u4EorL&0x01)?"ED":"LD",((u4EorL&0x02)>>1)?"ED":"LD");

	vIO32WriteFldAlign(SPI_CMD_REG, (u4MorL&0x01), FLD_SPI_TXMSBF);
	vIO32WriteFldAlign(SPI_CMD_REG, ((u4MorL&0x02)>>1), FLD_SPI_RXMSBF);
	vIO32WriteFldAlign(SPI_CMD_REG, (u4CS&0x01), FLD_SPI_CS_POL);
	vIO32WriteFldAlign(SPI_CMD_REG, (u4EorL&0x01), FLD_SPI_TX_ENDIAN);
	vIO32WriteFldAlign(SPI_CMD_REG, ((u4EorL&0x02)>>1), FLD_SPI_RX_ENDIAN);
	
	vIO32WriteFldAlign(SPI_CMD_REG, ((u4Mod&0x02)>>1), FLD_SPI_CPOL);
	vIO32WriteFldAlign(SPI_CMD_REG, (u4Mod&0x01), FLD_SPI_CPHA);

#ifdef PREPH_SPI_DMA_MOD
    vIO32WriteFldAlign(SPI_CMD_REG, 1, FLD_SPI_TX_DMA_EN);
    vIO32WriteFldAlign(SPI_CMD_REG, 1, FLD_SPI_RX_DMA_EN);
#else
    vIO32WriteFldAlign(SPI_CMD_REG, 0, FLD_SPI_TX_DMA_EN);
    vIO32WriteFldAlign(SPI_CMD_REG, 0, FLD_SPI_RX_DMA_EN);
#endif

#ifndef CC_MTK_LOADER
	if(BSP_GetIcVersion() >= IC_VER_5895_AB)
	{
		//enable int for spi
		vIO32WriteFldAlign(SPI_CMD_REG, 1, FLD_SPI_PAUSE_IE);
		vIO32WriteFldAlign(SPI_CMD_REG, 1, FLD_SPI_FINISH_IE);
		UNUSED(SpiSetIsr(TRUE));
		if(!_fgPrephSpiInit)
		{
			SpiIsrCreateSema();
		}
		_fgPrephSpiInit = TRUE;
	}
	else if(IS_IC_5895_ES1())
	{
		vIO32WriteFldAlign(SPI_CMD_REG, 1, FLD_SPI_PAUSE_IE);
		vIO32WriteFldAlign(SPI_CMD_REG, 1, FLD_SPI_FINISH_IE);
	}
#else
	vIO32WriteFldAlign(SPI_CMD_REG, 1, FLD_SPI_PAUSE_IE);
	vIO32WriteFldAlign(SPI_CMD_REG, 1, FLD_SPI_FINISH_IE);
#endif

#ifdef SPI_CI_COREHERENT_DMABUF
	SpiGetCorherentDMABuf();
#endif
}


//use coherent dma buf
#ifdef SPI_CI_COREHERENT_DMABUF
UINT32* SpiCorhTx = NULL;
UINT32* SpiCorhRx = NULL;
dma_addr_t spicorh_rx_addr;
dma_addr_t spicorh_tx_addr;


void SpiGetCorherentDMABuf(void)
{
	if(SpiCorhRx == NULL)
	{
		SpiCorhRx = dma_alloc_coherent(&(mt53xx_spi_device.dev),FIX_CI_DMA_BUF, &spicorh_rx_addr,GFP_KERNEL);
		if(SpiCorhRx == NULL)
		{
			LOG(0,"spi ci rx dma buf err\n");
		}
	}
	if(SpiCorhTx == NULL)
	{
		SpiCorhTx = dma_alloc_coherent(&(mt53xx_spi_device.dev),FIX_CI_DMA_BUF, &spicorh_tx_addr,GFP_KERNEL);
		if(SpiCorhTx == NULL)
		{
			LOG(0,"spi ci tx dma buf err\n");
		}
		
		memset(SpiCorhTx,0x5a,FIX_CI_DMA_BUF);	
	}

}
static INT32 SpiReadWriteCorherentDmaBuf(UINT32 fgReadWrite,UINT32 u4DataLen,UINT32 *u4RdBuf, UINT32 *u4WrBuf,UINT32 u4PackMode)
{
	INT32 i=0;
	INT32 i4Ret=0;
	UINT32 u4Size=0;
	UINT32 u4DataLoop=0;
#ifndef PREPH_SPI_DMA_MOD	
    UINT32 u4TxDumy[8]={0x5a5a5a5a,0x5a5a5a5a,0x5a5a5a5a,0x5a5a5a5a,0x5a5a5a5a,0x5a5a5a5a,0x5a5a5a5a,0x5a5a5a5a};
#else

#endif

#ifndef PREPH_SPI_DMA_MOD	
	if(fgReadWrite ==0 || u4DataLen ==0 || u4DataLen > 32)
	{
		LOG(0,"arg err with fifo mode %d buffer size 32byte rwflag(1-3):%d  size:%d!!\n",u4DataLen,fgReadWrite,u4Size);
		return -1;
	}
#else
    if(fgReadWrite ==0 || u4DataLen ==0)
    {
    	LOG(0,"arg error with dma mode len:%d rwflag(1-3):%d  size:%d!!\n",u4DataLen,fgReadWrite,u4DataLen);
    	return -1;
    }
#endif

	u4Size=((u4DataLen+3)/4);
#ifndef PREPH_SPI_DMA_MOD

	vIO32WriteFldAlign(SPI_CFG1_REG,u4DataLen-1,FLD_SPI_PACKET_LENGTH);
	vIO32WriteFldAlign(SPI_CFG1_REG,0,FLD_SPI_PACKET_LOOP_CNT);
#else
	if(u4Size > PREPH_SPI_DMA_PACKET_SIZE)
	{
		u4DataLoop = (u4Size/PREPH_SPI_DMA_PACKET_SIZE)*4;
		vIO32WriteFldAlign(SPI_CFG1_REG,(PREPH_SPI_DMA_PACKET_SIZE-1),FLD_SPI_PACKET_LENGTH);
		vIO32WriteFldAlign(SPI_CFG1_REG,u4DataLoop-1,FLD_SPI_PACKET_LOOP_CNT);
		//vIO32Write4B(SPI_CFG1_REG,(u4IO32Read4B(SPI_CFG1_REG)&0xFC00FFFF)|((u4DataLen-1) << 16));
		//every packet is packet align here
		//coop  last patcket in SpiPackPrepare function
		//dispart these large packet to two packet
		//align packet and a rear packet which data length small than one packet 
	}
	else
	{
		vIO32WriteFldAlign(SPI_CFG1_REG,u4DataLen-1,FLD_SPI_PACKET_LENGTH);
		vIO32WriteFldAlign(SPI_CFG1_REG,0,FLD_SPI_PACKET_LOOP_CNT);
	}
#endif
	vIO32Write4B(SPI_CMD_REG,u4IO32Read4B(SPI_CMD_REG)&0xfffffff7);
	
	vIO32WriteFldAlign(SPI_CMD_REG, 0, FLD_SPI_TX_DMA_EN);
	vIO32WriteFldAlign(SPI_CMD_REG, 0, FLD_SPI_RX_DMA_EN);
	
	if(fgReadWrite==SPI_READ)//rx_only
	{	
	//spi rx only not work in pause mode, so we need manually build a dummy data 
#ifndef PREPH_SPI_DMA_MOD
		for(i=0;i<u4Size;i++)
		{
			vIO32Write4B(SPI_TX_DATA_REG,u4TxDumy[i]);
			LOG(1,"use tx dummy data for rx only\n");
		}
#else
	
		vIO32Write4B(SPI_TX_SRC_REG,spicorh_tx_addr);

		vIO32Write4B(SPI_RX_DST_REG,spicorh_rx_addr);
			
		vIO32WriteFldAlign(SPI_CMD_REG, 1, FLD_SPI_TX_DMA_EN);
		vIO32WriteFldAlign(SPI_CMD_REG, 1, FLD_SPI_RX_DMA_EN);
#endif
	}
	else if(fgReadWrite==SPI_WRITE)//tx only
	{
		
#ifndef PREPH_SPI_DMA_MOD
		for(i=0;i<u4Size;i++)
		{
			vIO32Write4B(SPI_TX_DATA_REG,u4WrBuf[i]);
			//LOG(0,"0x%x  \n",u4WrBuf[i]);
		}
#else		
		memcpy((void*)SpiCorhTx,(void*)u4WrBuf,u4DataLen);

		vIO32Write4B(SPI_TX_SRC_REG,spicorh_tx_addr);
		vIO32WriteFldAlign(SPI_CMD_REG, 1, FLD_SPI_TX_DMA_EN);
#endif			
	}
	else 
	{

#ifndef PREPH_SPI_DMA_MOD
	for(i=0;i<u4Size;i++)
	{
		vIO32Write4B(SPI_TX_DATA_REG,u4WrBuf[i]);
		//LOG(0,"0x%x  \n",u4WrBuf[i]);
	}
#else

		memcpy((void*)SpiCorhTx,(void*)u4WrBuf,u4DataLen);
		vIO32Write4B(SPI_TX_SRC_REG,spicorh_tx_addr);
		vIO32Write4B(SPI_RX_DST_REG,spicorh_rx_addr);
		vIO32WriteFldAlign(SPI_CMD_REG, 1, FLD_SPI_TX_DMA_EN);
		vIO32WriteFldAlign(SPI_CMD_REG, 1, FLD_SPI_RX_DMA_EN);

#endif			
 	}
	vIO32Write4B(SPI_CMD_REG,(u4IO32Read4B(SPI_CMD_REG)&0xffffffef)|(u4PackMode&0x10)); //pause mode.
	vIO32Write4B(SPI_CMD_REG,(u4IO32Read4B(SPI_CMD_REG)&0xfffffffc)|(u4PackMode&0x03)); //Trigger SPI.

#ifdef CC_MTK_LOADER
	while(((u4IO32Read4B(SPI_STATUS1_REG)&0x01) ==0x0) && (i<SPI_LOOP_TIMEOUT)) //wait status if you want send more bytes,plz make sure wait enough time.
	{
		//x_thread_delay(20);
		//HAL_Delay_us(20);  //this thread grap the cpu
		//x_thread_delay(1); //euql msleep(1);
		//x_thread_delay(0); //yield(); this thread is keep in running table
		i++;
	}
	if(i==SPI_LOOP_TIMEOUT)
	{
		LOG(0,"spi busy\n");
		goto err_free;
	}
#else
	if(IS_IC_5895_ES1())
	{
		while(((u4IO32Read4B(SPI_STATUS1_REG)&0x01) ==0x0) && (i<SPI_LOOP_TIMEOUT)) //wait status if you want send more bytes,plz make sure wait enough time.
		{
			//x_thread_delay(20);
			//HAL_Delay_us(20);  //this thread grap the cpu
			//x_thread_delay(1); //euql msleep(1);
			//x_thread_delay(0); //yield(); this thread is keep in running table
			i++;
		}
		if(i==SPI_LOOP_TIMEOUT)
		{
			LOG(0,"spi busy\n");
			goto err_free;
		}
	}
	else if(BSP_GetIcVersion() >= IC_VER_5895_AB)
	{
		/*if (SpiIsrReinnitLock() != OSR_WOULD_BLOCK)
		{
			LOG(0,"SPI ISR lock status error!\n");
			goto err_free;
		}*/
		i4Ret = SpiIsrLock();
		if (i4Ret != OSR_OK)
		{
			LOG(0,"i4Ret=%d,spi wait hw time out!!!\n",i4Ret);
			goto err_free;
		}
	}
#endif

#ifndef PREPH_SPI_DMA_MOD
	if((u4RdBuf != NULL) && (fgReadWrite & SPI_READ)) //GSPI Read enable.
	{
		for(i=0;i<u4Size;i++)
		{
			u4RdBuf[i] = u4IO32Read4B(SPI_RX_DATA_REG);			
			//LOG(0,"0x%x  \n",u4RdBuf[i]);
		}
	}
#else
	
	if(fgReadWrite==SPI_READ)
	{		
		memcpy((void*)u4RdBuf,(void*)SpiCorhRx,u4DataLen);
	}	
	else if(fgReadWrite==SPI_WRITE)
	{
		
	}
	else
	{	
		memcpy((void*)u4RdBuf,(void*)SpiCorhRx,u4DataLen);
	}
	
#endif
	
	return 0;
err_free:	
	return -1;
}
#else
static INT32 SpiReadWriteSingleDmaBuf(UINT32 fgReadWrite,UINT32 u4DataLen,UINT32 *u4RdBuf, UINT32 *u4WrBuf,UINT32 u4PackMode)
{
	INT32 i=0;
	UINT32 u4Size=0;
	UINT32 u4DataLoop=0;
#ifndef PREPH_SPI_DMA_MOD	
    UINT32 u4TxDumy[8]={0x5a5a5a5a,0x5a5a5a5a,0x5a5a5a5a,0x5a5a5a5a,0x5a5a5a5a,0x5a5a5a5a,0x5a5a5a5a,0x5a5a5a5a};
#else
	UINT32 *u4TxDumy;
	dma_addr_t rxRet;
	dma_addr_t txRet;
#endif

#ifndef PREPH_SPI_DMA_MOD	
	if(fgReadWrite ==0 || u4DataLen ==0 || u4DataLen > 32)
	{
		LOG(0,"arg err with fifo mode %d buffer size 32byte rwflag(1-3):%d  size:%d!!\n",u4DataLen,fgReadWrite,u4Size);
		return -1;
	}
#else
    if(fgReadWrite ==0 || u4DataLen ==0)
    {
    	LOG(0,"arg error with dma mode len:%d rwflag(1-3):%d  size:%d!!\n",u4DataLen,fgReadWrite,u4Size);
    	return -1;
    }
#endif
	u4Size=((u4DataLen+3)/4);
#ifndef PREPH_SPI_DMA_MOD

	vIO32WriteFldAlign(SPI_CFG1_REG,u4DataLen-1,FLD_SPI_PACKET_LENGTH);
	vIO32WriteFldAlign(SPI_CFG1_REG,0,FLD_SPI_PACKET_LOOP_CNT);
#else
	if(u4Size > PREPH_SPI_DMA_PACKET_SIZE)
	{
		u4DataLoop = (u4Size/PREPH_SPI_DMA_PACKET_SIZE)*4;
		vIO32WriteFldAlign(SPI_CFG1_REG,(PREPH_SPI_DMA_PACKET_SIZE-1),FLD_SPI_PACKET_LENGTH);
		vIO32WriteFldAlign(SPI_CFG1_REG,u4DataLoop-1,FLD_SPI_PACKET_LOOP_CNT);
		//vIO32Write4B(SPI_CFG1_REG,(u4IO32Read4B(SPI_CFG1_REG)&0xFC00FFFF)|((u4DataLen-1) << 16));
		//every packet is packet align here
		//coop  last patcket in SpiPackPrepare function
		//dispart these large packet to two packet
		//align packet and a rear packet which data length small than one packet 
	}
	else
	{
		vIO32WriteFldAlign(SPI_CFG1_REG,u4DataLen-1,FLD_SPI_PACKET_LENGTH);
		vIO32WriteFldAlign(SPI_CFG1_REG,0,FLD_SPI_PACKET_LOOP_CNT);
	}
#endif
	LOG(2,"size %d mod:0x%x loop cnt:0x%x cfg1:0x%x flag:0x%x!!\n",
		u4Size,u4PackMode,u4DataLoop,u4IO32Read4B(SPI_CFG1_REG),fgReadWrite);
	vIO32Write4B(SPI_CMD_REG,u4IO32Read4B(SPI_CMD_REG)&0xfffffff7);
	
	vIO32WriteFldAlign(SPI_CMD_REG, 0, FLD_SPI_TX_DMA_EN);
	vIO32WriteFldAlign(SPI_CMD_REG, 0, FLD_SPI_RX_DMA_EN);
	
	if(fgReadWrite==SPI_READ)//rx_only
	{	
	//spi rx only not work in pause mode, so we need manually build a dummy data 
#ifndef PREPH_SPI_DMA_MOD
		for(i=0;i<u4Size;i++)
		{
			vIO32Write4B(SPI_TX_DATA_REG,u4TxDumy[i]);
			LOG(1,"use tx dummy data for rx only\n");
		}
#else
		//spi dma can only access emi 1G-4G 
		//to make spi api can be called from atomic context,so need GFP_ATOMIC
#ifndef SPI_FIXBUF_FORDUMMY
		u4TxDumy = kmalloc(sizeof(UINT32)*u4Size,(GFP_ATOMIC));
#endif
		if(!u4TxDumy)
		{
			LOG(0,"kmalloc tx dummy buf fail\n");
			return -1;
		}

#ifndef SPI_FIXBUF_FORDUMMY
		memset(u4TxDumy,0x5a,sizeof(INT32)*u4Size);
#endif

		txRet = SpiDmaMap(u4TxDumy,sizeof(INT32)*u4Size,TO_DEVICE);
		if(!txRet)
		{
			goto err_free;
		}
		rxRet = SpiDmaMap(u4RdBuf,sizeof(INT32)*u4Size,FROM_DEVICE);
		if(!rxRet)
		{
			goto err_free;
		}
		
		vIO32WriteFldAlign(SPI_CMD_REG, 1, FLD_SPI_TX_DMA_EN);
		vIO32WriteFldAlign(SPI_CMD_REG, 1, FLD_SPI_RX_DMA_EN);
#endif
	}
	else if(fgReadWrite==SPI_WRITE)//tx only
	{
		
#ifndef PREPH_SPI_DMA_MOD
			for(i=0;i<u4Size;i++)
			{
				vIO32Write4B(SPI_TX_DATA_REG,u4WrBuf[i]);
				//LOG(0,"0x%x  \n",u4WrBuf[i]);
			}
#else		

			LOG(2,"txonly map wr 0x%p size %d \n",u4WrBuf,u4Size*4);
			txRet = SpiDmaMap(u4WrBuf,sizeof(INT32)*u4Size,TO_DEVICE);
			if(!txRet)
			{
				goto err_free;
			}	
		
			vIO32WriteFldAlign(SPI_CMD_REG, 1, FLD_SPI_TX_DMA_EN);
#endif			
	}
	else 
	{

#ifndef PREPH_SPI_DMA_MOD
	for(i=0;i<u4Size;i++)
	{
		vIO32Write4B(SPI_TX_DATA_REG,u4WrBuf[i]);
		//LOG(0,"0x%x  \n",u4WrBuf[i]);
	}
#else		
		
		LOG(2,"map wr 0x%p size %d \n",u4WrBuf,u4Size*4);
		txRet = SpiDmaMap(u4WrBuf,sizeof(INT32)*u4Size,TO_DEVICE);
		if(!txRet)
		{
			goto err_free;
		}
		
	LOG(2,"map rx 0x%p size %d \n",u4RdBuf,u4Size*4);
		rxRet = SpiDmaMap(u4RdBuf,sizeof(INT32)*u4Size,FROM_DEVICE);				
		if(!rxRet)
		{
			goto err_free;
		}
		
		vIO32WriteFldAlign(SPI_CMD_REG, 1, FLD_SPI_TX_DMA_EN);
		vIO32WriteFldAlign(SPI_CMD_REG, 1, FLD_SPI_RX_DMA_EN);

#endif			
 	}
	vIO32Write4B(SPI_CMD_REG,(u4IO32Read4B(SPI_CMD_REG)&0xffffffef)|(u4PackMode&0x10)); //pause mode.
	LOG(2,"b4 trigger write 0x%x --> 0x%x",u4IO32Read4B(SPI_CMD_REG),(u4IO32Read4B(SPI_CMD_REG)&0xfffffffc)|(u4PackMode&0x03));
	vIO32Write4B(SPI_CMD_REG,(u4IO32Read4B(SPI_CMD_REG)&0xfffffffc)|(u4PackMode&0x03)); //Trigger SPI.

#ifdef CC_MTK_LOADER
	while(((u4IO32Read4B(SPI_STATUS1_REG)&0x01) ==0x0) && (i<SPI_LOOP_TIMEOUT)) //wait status.
	{
		//x_thread_delay(20);
		//HAL_Delay_us(5);	//this thread grap the cpu
		//x_thread_delay(1); //euql msleep(1);
		//x_thread_delay(0); //yield(); this thread is keep in running table
		i++;
	}
	if(i==SPI_LOOP_TIMEOUT)
	{
		LOG(0,"spi busy\n");
		goto err_free;
	}
#else
	if(IS_IC_5895_ES1())
	{
		while(((u4IO32Read4B(SPI_STATUS1_REG)&0x01) ==0x0) && (i<SPI_LOOP_TIMEOUT)) //wait status.
		{
			//x_thread_delay(20);
			//HAL_Delay_us(5);  //this thread grap the cpu
			//x_thread_delay(1); //euql msleep(1);
			//x_thread_delay(0); //yield(); this thread is keep in running table
			i++;
		}
		if(i==SPI_LOOP_TIMEOUT)
		{
			LOG(0,"spi busy\n");
			goto err_free;
		}
	}
	else if(BSP_GetIcVersion() >= IC_VER_5895_AB)
	{
		/*if (SpiIsrReinnitLock() != OSR_WOULD_BLOCK)
		{
			LOG(0,"SPI ISR lock status error!\n");
			goto err_free;
		}*/
		if (SpiIsrLock() != OSR_OK)
		{
			LOG(0,"spi wait hw time out!!!\n");
			goto err_free;
		}
	}
#endif
	LOG(2,"write done delay %d us!!!\n",i*20);
#ifndef PREPH_SPI_DMA_MOD
	if((u4RdBuf != NULL) && (fgReadWrite & SPI_READ)) //GSPI Read enable.
	{
		for(i=0;i<u4Size;i++)
		{
			u4RdBuf[i] = u4IO32Read4B(SPI_RX_DATA_REG);			
			//LOG(0,"0x%x  \n",u4RdBuf[i]);
		}
	}
#else
	
	if(fgReadWrite==SPI_READ)
	{
		//rx_only unmap tx dummy buf
		SpiDmaUnmap(txRet,sizeof(INT32)*u4Size,TO_DEVICE);		
		SpiDmaUnmap(rxRet,sizeof(INT32)*u4Size,FROM_DEVICE);		
	}	
	else if(fgReadWrite==SPI_WRITE)
	{
		SpiDmaUnmap(txRet,sizeof(INT32)*u4Size,TO_DEVICE);			
	}
	else
	{
		SpiDmaUnmap(txRet,sizeof(INT32)*u4Size,TO_DEVICE);			
		SpiDmaUnmap(rxRet,sizeof(INT32)*u4Size,FROM_DEVICE);		
	}
	
#endif
	
	return 0;
err_free:	
	//no need to unmap when error because dma_map_error
#ifdef PREPH_SPI_DMA_MOD
#ifndef SPI_FIXBUF_FORDUMMY
	if(fgReadWrite == SPI_READ)//rx_only
		kfree(u4TxDumy);
#endif
#endif
	SpiDump();
	return -1;
}
#endif

static INT32 SpiReadWrite(UINT32 fgReadWrite,UINT32 u4DataLen,UINT32 *u4RdBuf, UINT32 *u4WrBuf,UINT32 u4PackMode)
{
	INT32 i4Ret,i,times;
	UINT32 OldDataLen = u4DataLen;
	UINT32 *OldRdBuf = u4RdBuf;
	UINT32 *OldWrBuf = u4WrBuf;
	UINT32 FirstMode  = u4PackMode;
	UINT32 OldMode  = u4PackMode;
	UINT32 CurSize = PREPH_SPI_DMA_PACKET_SIZE;
	i=0;
	times = (u4DataLen+PREPH_SPI_DMA_PACKET_SIZE-1)/PREPH_SPI_DMA_PACKET_SIZE;
	
	//LOG(0,"first rdbuf %p wrbuf %p total len 0x%x\n",OldRdBuf,OldWrBuf,u4DataLen);
	while(u4DataLen > 0)
	{
		if(FirstMode == ONE_PACK)
		{
			if(OldDataLen > PREPH_SPI_DMA_PACKET_SIZE)
			{
				CurSize = PREPH_SPI_DMA_PACKET_SIZE;
    			if(i==0)
    				OldMode=FIRST_PACK;
    			else if((i+1) == times)
    				OldMode=LAST_PACK;
    			else
    				OldMode=MID_PACK;
			}
			else
			{
				OldMode = ONE_PACK;
			}
		}
		else if(FirstMode == FIRST_PACK)
		{
			if(OldDataLen > PREPH_SPI_DMA_PACKET_SIZE)
			{			
				CurSize = PREPH_SPI_DMA_PACKET_SIZE;
    			if(i==0)
    				OldMode=FIRST_PACK;
    			else
    				OldMode=MID_PACK;
			}
			else
			{
				OldMode = FIRST_PACK;
			}
		}
		else if(FirstMode == MID_PACK)
		{		
			OldMode = MID_PACK;
		}
		else if(FirstMode == LAST_PACK)
		{
			if(OldDataLen > PREPH_SPI_DMA_PACKET_SIZE)
			{
    			if((i+1) == times)
    				OldMode=LAST_PACK;
    			else
    				OldMode=MID_PACK;
			}
			else		
			{
				OldMode = LAST_PACK;
			}
		}
		
		if(u4DataLen > PREPH_SPI_DMA_PACKET_SIZE)
			CurSize = PREPH_SPI_DMA_PACKET_SIZE;
		else
			CurSize = u4DataLen;
		
		//LOG(0,"rdbuf %p wrbuf %p curlen 0x%x left len 0x%x\n",OldRdBuf,OldWrBuf,CurSize,u4DataLen);
    #ifdef SPI_CI_COREHERENT_DMABUF
    	i4Ret = SpiReadWriteCorherentDmaBuf(fgReadWrite,CurSize,OldRdBuf,OldWrBuf,OldMode);
    #else
    	i4Ret = SpiReadWriteSingleDmaBuf(fgReadWrite,CurSize,OldRdBuf,OldWrBuf,OldMode);
    #endif
   		u4DataLen -= CurSize;
	if(u4DataLen !=0)
	{
		OldRdBuf += (CurSize/4);
		OldWrBuf += (CurSize/4);
	}
		//LOG(0,"first mod 0x%x loop %d total loop %d fixed mode is 0x%x last sendlen %d left datalen %d\n",FirstMode,i,times,OldMode,CurSize,u4DataLen);
		i++;
	}
	
	return i4Ret;
}
SPIPACK_T *CurPackBuf;
BOOL SpiPackCheck(SPIPACK_T *GetPackBuf)
{
	if((GetPackBuf->RdBuf == NULL) && (GetPackBuf->WrRd & SPI_READ))
	{
		LOG(0,"chk rd buf error\n");
		return FALSE;
	}
	if((GetPackBuf->WrBuf == NULL) && (GetPackBuf->WrRd & SPI_WRITE))
	{
		LOG(0,"chk wr buf error\n");
		return FALSE;
	}
	return TRUE;
}

#ifdef SPI_PERFORMACE_TEST_BY_HWTIMER
UINT32 spivv1,spivv2,spivv3,spivv4,spivv5;
#endif
INT32 SpiReadWriteMultiPack(UINT32 u4PackLen, SPIPACK_T *PackBuf, PREPH_SPI_CONF *SpiInitCon)
{
	UINT32 i=0;
	UINT32 PackMod;
	static PREPH_SPI_CONF PreDeviceCon;
#ifdef SPI_PERFORMACE_TEST_BY_HWTIMER
	spivv3=u4IO32Read4B(PDWNC_WDT0);
#endif

	if(!IS_IC_5895())
	{
		LOG(0,"just 5895 support this preph spi\n");
		return -1;
	}
#ifdef SPI_PERFORMACE_TEST_BY_HWTIMER
	spivv4=u4IO32Read4B(PDWNC_WDT0);
#endif
	if(!PackBuf || u4PackLen < 1)
	{
		LOG(0,"pack format error\n");
		return -1;
	}
	//vIO32Write4B(SPI_CMD_REG,0x04);	
	//vIO32Write4B(SPI_CMD_REG,0x0);  //reset b4 trans
#ifndef CC_MTK_LOADER
#if defined(CC_SUPPORT_STR_CORE_OFF)
	if(SpiIssuspended())		
	{
		LOG(0,"spi already suspended\n");
		return -1;
	}
#endif
#ifdef	SPI_USE_SEMA
	if(!SpiUserSema)
		VERIFY(x_sema_create(&SpiUserSema, X_SEMA_TYPE_BINARY, X_SEMA_STATE_UNLOCK) == OSR_OK);
	//spi is  serial interface so must get the user sema first
    VERIFY(x_sema_lock_timeout(SpiUserSema, SPI_USER_SEMA_TIMEOUT) == OSR_OK);
#endif
#endif

#ifdef SPI_PERFORMACE_TEST_BY_HWTIMER
	spivv5=u4IO32Read4B(PDWNC_WDT0);
#endif
	if(!SpiInitCon || !(memcmp(&PreDeviceCon,SpiInitCon,sizeof(PREPH_SPI_CONF))))
	{	
		LOG(4,"spi config null or same,just use current spi setting!!!\n");
	}
	else
	{
    	SpiInit(SpiInitCon);
		memcpy(&PreDeviceCon,SpiInitCon,sizeof(PREPH_SPI_CONF));
	}
#ifdef SPI_PERFORMACE_TEST_BY_HWTIMER	
	spivv1=u4IO32Read4B(PDWNC_WDT0);
#endif
	for(i=0;i<u4PackLen;i++)
	{
		CurPackBuf=(PackBuf+i);

		//should make sure read buf and write buf should not be null if read and write flag is seted
		if(!SpiPackCheck(CurPackBuf))
			break;
		
		LOG(2,"pack %d len %d\n",i,CurPackBuf->DataLen);
		if(u4PackLen==1)
			PackMod=ONE_PACK;
		else
		{
    		if(i==0)
    			PackMod=FIRST_PACK;
    		else if((i+1) == u4PackLen)
    			PackMod=LAST_PACK;
    		else
    			PackMod=MID_PACK;
		}
		
		if(0>SpiReadWrite(CurPackBuf->WrRd,CurPackBuf->DataLen,CurPackBuf->RdBuf,CurPackBuf->WrBuf,PackMod))
		{
			LOG(0,"spi trans err when %d pack trasf, len:%d wrbuf:%p rdbuf:%p\n",i,CurPackBuf->DataLen,CurPackBuf->WrBuf,CurPackBuf->RdBuf);
			break;
		}
		
	}
#ifdef SPI_PERFORMACE_TEST_BY_HWTIMER
	spivv2=u4IO32Read4B(PDWNC_WDT0);
#endif
	if(i!=u4PackLen)
	{
		LOG(0,"multi pack send failed! reset master!\n");
		SpiDump();
		SpiReset();
	}
	

#ifndef CC_MTK_LOADER
#ifdef	SPI_USE_SEMA
err_out:
	VERIFY(x_sema_unlock(SpiUserSema) == OSR_OK);
#endif
#endif
	return 0;
}

#ifndef CC_MTK_LOADER
#if defined(CC_SUPPORT_STR_CORE_OFF)
static void SpiLastTransfDone(void)
{
#ifdef	SPI_USE_SEMA
    VERIFY(x_sema_lock_timeout(SpiUserSema, SPI_USER_SEMA_TIMEOUT) == OSR_OK);	
#endif
	SpiSetsuspended();
#ifdef	SPI_USE_SEMA
    VERIFY(x_sema_unlock(SpiUserSema) == OSR_OK);
#endif
}

//suspend and resume function
void GSPI_pm_suspend(void)
{
	SpiLastTransfDone();
}

void GSPI_pm_resume(void)
{
	SpiClrsuspended();
}
#endif
#endif


