//-------------------------------------------------------------------------------------------------------------------------
// Copyright (c) Samsung Electronics Co., Ltd.  All rights reserved.
//-------------------------------------------------------------------------------------------------------------------------
//
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED,
// INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
//
// Module Name :    SPI.C
//
// Abstract     :   SPI Interface Routines for Samsung S5PV210 CPU
//
// Environment :    Samsung S5PV210 / WincCE6.0
//
// 2010/02/10 asdf  Added Full duplex mode (Ver 1.01)
//                          Define SPI_FULL_DUPLEX in sources file to use full duplex
// 2009/07/10 asdf  Modified for supporting multi-channel
//
//-------------------------------------------------------------------------------------------------------------------------



/*****************************************************************************
 * Includes
 *****************************************************************************/ 

#include <windows.h>
#include <nkintr.h>
#include <ceddk.h>   
#include <memory.h>
#include <types.h>

#include <ddkreg.h>
#include <bsp.h>
#include <oal_intr.h>
#include <drvmsg.h>
#include <dma_controller.h>
#include <spi_reg.h>
#include "spi_priv.h"
#include <pmplatform.h>
#ifdef SPI_COMM_STREAM_IF
//#include <serpriv.h>
#endif 


/*****************************************************************************
 * Definitions
 *****************************************************************************/ 
//#define SLAVE_READY  0
//#define TEST_MODE
// MSG
#define SPI_MSG     SPI_USR1
#define SPI_INIT    SPI_INFO
#define SPI_DEBUG	SPI_DBG

#define VERSION_INFO		"SPI"
#define DateInformation		"1.03"

#define SPI_CLOCK	MPLL_CLOCK  //EPLL_CLOCK//MPLL_CLOCK
#define CLKSEL      (0x1 << 9)  //SCLK



/*****************************************************************************
 * Functions 
 *****************************************************************************/ 

DWORD HW_Init(PSPI_PUBLIC_CONTEXT pPublicSpi);
DWORD ThreadForTx(PSPI_PUBLIC_CONTEXT pPublicSpi);
DWORD ThreadForRx(PSPI_PUBLIC_CONTEXT pPublicSpi);
DWORD ThreadForSpi(PSPI_PUBLIC_CONTEXT pPublicSpi);


#ifdef SPI_EX_GPIO_CALLBACK
extern BOOL HSPCALLBACK_SetSlaveReady(PSPI_PRIVATE_CONTEXT pSpiPrivate); // SLAVE SIDE
extern BOOL HSPCALLBACK_ClrSlaveReady(PSPI_PRIVATE_CONTEXT pSpiPrivate);
extern BOOL HSPCALLBACK_GetSlaveReady(PSPI_PRIVATE_CONTEXT pSpiPrivate);
extern BOOL HSPCALLBACK_Init(PSPI_PRIVATE_CONTEXT pSpiPrivate);
extern BOOL HSPCALLBACK_Deinit(PSPI_PRIVATE_CONTEXT pSpiPrivate);
#endif 

#ifdef SPI_FULL_DUPLEX
BOOL SPI_FullDuplex(PSPI_PRIVATE_CONTEXT pSpiPrivate, PBYTE pRxBuf, PBYTE pTxBuf, DWORD dwCount);
#endif 

void SPI_SFR_DUMP(PSPI_PUBLIC_CONTEXT pPublicSpi)
{
#if 0 
    volatile SPI_REG    *pSPIregs  = pPublicSpi->pSPIregs; 
    DBGMSG(SPI_INFO,(TEXT("**** SPI SFR CH%d*****\r\n"), pPublicSpi->chnum));
    DBGMSG(SPI_INFO,(TEXT(" CH_CFG : 0x%x \r\n"), pSPIregs->CH_CFG));
    DBGMSG(SPI_INFO,(TEXT(" CLK_CFG : 0x%x \r\n"), pSPIregs->CLK_CFG));
    DBGMSG(SPI_INFO,(TEXT(" MODE_CFG : 0x%x \r\n"), pSPIregs->MODE_CFG));
    DBGMSG(SPI_INFO,(TEXT(" SPI_INT_EN : 0x%x \r\n"), pSPIregs->SPI_INT_EN));
    DBGMSG(SPI_INFO,(TEXT(" STATUS : 0x%x \r\n"), pSPIregs->SPI_STATUS));
    DBGMSG(SPI_INFO,(TEXT(" FB CLK DELAY : 0x%x \r\n"), pSPIregs->FB_CLK_SEL));
#endif 
}


BOOL
DllEntry(
		HINSTANCE   hinstDll,
		DWORD       dwReason,
		LPVOID      lpReserved
	)
{
	if ( dwReason == DLL_PROCESS_ATTACH )
	{
		DBGMSG (SPI_DEBUG, (TEXT("[SPI] Process Attach\r\n")));
	}

	if ( dwReason == DLL_PROCESS_DETACH )
	{
		DBGMSG (SPI_DEBUG, (TEXT("[SPI] Process Detach\r\n")));
	}

	return(TRUE);
}

DWORD
HW_Init(
		PSPI_PUBLIC_CONTEXT pPublicSpi
       )
{
	BOOL bResult = TRUE;
	PHYSICAL_ADDRESS ioPhysicalBase = {0, 0};

	if ( !pPublicSpi )
	{
		bResult = FALSE;
		goto CleanUp;
	}

	// GPIO Virtual alloc
	ioPhysicalBase.LowPart = BASE_REG_PA_GPIO;
	pPublicSpi->pGPIOregs = (PGPIO_REG)MmMapIoSpace(ioPhysicalBase, sizeof(GPIO_REG), FALSE);
	if (pPublicSpi->pGPIOregs == NULL)
	{
		ERRMSG((TEXT("[SPI] For pGPIOregs: MmMapIoSpace failed!\r\n")));
		bResult = FALSE;
		goto CleanUp;
	}

	// Syscon Virtual alloc
	ioPhysicalBase.LowPart = BASE_REG_PA_CMU_CLK;
	pPublicSpi->pSYSCONregs = (PCMU_CLK_REG)MmMapIoSpace(ioPhysicalBase, sizeof(CMU_CLK_REG), FALSE);
	if (pPublicSpi->pSYSCONregs == NULL)
	{
		ERRMSG((TEXT("[SPI] For pSYSCONregs: MmMapIoSpace failed!\r\n")));
		bResult = FALSE;
		goto CleanUp;
	}

	// MDMAC Virtual alloc
	ioPhysicalBase.LowPart = BASE_REG_PA_MDMA;
	pPublicSpi->pDMACregs = (PDMAC_REG)MmMapIoSpace(ioPhysicalBase, sizeof(DMAC_REG), FALSE);
	if (pPublicSpi->pDMACregs == NULL)
	{
		ERRMSG((TEXT("[SPI] For pDMACregs: MmMapIoSpace failed!\r\n")));
		bResult = FALSE;
		goto CleanUp;
	}

	// DMAC0 Virtual alloc
	ioPhysicalBase.LowPart = BASE_REG_PA_PDMA0;
	pPublicSpi->pDMAC0regs = (PDMAC_REG)MmMapIoSpace(ioPhysicalBase, sizeof(DMAC_REG), FALSE);
	if (pPublicSpi->pDMAC0regs == NULL)
	{
		ERRMSG((TEXT("[SPI] For pDMAC0regs: MmMapIoSpace failed!\r\n")));
		bResult = FALSE;
		goto CleanUp;
	}

	// DMAC1 Virtual alloc
	ioPhysicalBase.LowPart = BASE_REG_PA_PDMA1;
	pPublicSpi->pDMAC1regs = (PDMAC_REG)MmMapIoSpace(ioPhysicalBase, sizeof(DMAC_REG), FALSE);
	if (pPublicSpi->pDMAC1regs == NULL)
	{
		ERRMSG((TEXT("[SPI] For pDMAC1regs: MmMapIoSpace failed!\r\n")));
		bResult = FALSE;
		goto CleanUp;
	}


	// HS-SPI Virtual alloc
	ioPhysicalBase.LowPart = BASE_REG_PA_SPI0+(pPublicSpi->chnum * BASE_REG_SPI_OFFSET);

	pPublicSpi->pSPIregs = (PSPI_REG)MmMapIoSpace(ioPhysicalBase, sizeof(SPI_REG), FALSE);
	if (pPublicSpi->pSPIregs == NULL)
	{
		ERRMSG((TEXT("[SPI] For pSPIregs: MmMapIoSpace failed!\r\n")));
		bResult = FALSE;
		goto CleanUp;
	}

	if(pPublicSpi->chnum==SPI0)  
	{
		DBGMSG(SPI_INFO,(TEXT("[SPI] CH0 HW_init\r\n")));
		//Configure SPI Port Drive Strength
		pPublicSpi->pGPIOregs->GPB.GP_DRV_SR = (pPublicSpi->pGPIOregs->GPB.GP_DRV_SR & ~(0xFF<<0)) | (0x55<<0);

		//Set GPIO for MISO, MOSI, SPICLK, SS
		Set_PinFunction(pPublicSpi->pGPIOregs, SPI0_CLK);
		Set_PinFunction(pPublicSpi->pGPIOregs, SPI0_nSS);
		Set_PinFunction(pPublicSpi->pGPIOregs, SPI0_MISO);
		Set_PinFunction(pPublicSpi->pGPIOregs, SPI0_MOSI); 
             Set_PinPullUD(pPublicSpi->pGPIOregs, SPI0_CLK, sgip_PULL_DISABLE);
             Set_PinPullUD(pPublicSpi->pGPIOregs, SPI0_nSS, sgip_PULL_DISABLE);
             Set_PinPullUD(pPublicSpi->pGPIOregs, SPI0_MISO, sgip_PULL_DISABLE);
             Set_PinPullUD(pPublicSpi->pGPIOregs, SPI0_MOSI, sgip_PULL_DISABLE);    
         	DBGMSG(SPI_INFO,(TEXT("++[SPI] SPI_GPIO GPB_DRV_SR =%x, GPB_PUD = %x, GPB_CON = %x\r\n")
			, pPublicSpi->pGPIOregs->GPB.GP_DRV_SR
			, pPublicSpi->pGPIOregs->GPB.GP_PUD
			, pPublicSpi->pGPIOregs->GPB.GP_CON)); 

		// Clock On
#if (SPI_CLOCK == EPLL_CLOCK)
		pPublicSpi->pSYSCONregs->CLK_SRC.CLK_SRC5      = (pPublicSpi->pSYSCONregs->CLK_SRC.CLK_SRC5 & ~(0xF << 0)) | (0x7 << 0);
		pPublicSpi->pSYSCONregs->CLK_SRC.CLK_SRC_MASK0 = (pPublicSpi->pSYSCONregs->CLK_SRC.CLK_SRC_MASK0 | (1 << 16));
		pPublicSpi->pSYSCONregs->CLK_DIV.CLK_DIV5      = (pPublicSpi->pSYSCONregs->CLK_DIV.CLK_DIV5 & ~(0xF << 0));
#elif (SPI_CLOCK == MPLL_CLOCK)
		pPublicSpi->pSYSCONregs->CLK_SRC.CLK_SRC5      = (pPublicSpi->pSYSCONregs->CLK_SRC.CLK_SRC5 & ~(0xF << 0)) | (0x6 << 0); 
		pPublicSpi->pSYSCONregs->CLK_SRC.CLK_SRC_MASK0 = (pPublicSpi->pSYSCONregs->CLK_SRC.CLK_SRC_MASK0 | (1 << 16));
		pPublicSpi->pSYSCONregs->CLK_DIV.CLK_DIV5      = (pPublicSpi->pSYSCONregs->CLK_DIV.CLK_DIV5 & ~(0xF << 0)) | (7 << 0);
#endif

		pPublicSpi->pSYSCONregs->CLK_GATE.CLK_GATE_IP3 = (pPublicSpi->pSYSCONregs->CLK_GATE.CLK_GATE_IP3 & ~(0x1 << 12)) | (0x1 << 12);

	}
	else if(pPublicSpi->chnum==SPI1)
	{
		DBGMSG(SPI_INFO,(TEXT("[SPI] CH1 HW_init\r\n")));
		//Configure SPI Port Drive Strength
		pPublicSpi->pGPIOregs->GPB.GP_DRV_SR = (pPublicSpi->pGPIOregs->GPB.GP_DRV_SR & ~(0xFF<<8)) | (0xFF<<8);

		//Set GPIO for MISO, MOSI, SPICLK, SS
		Set_PinFunction(pPublicSpi->pGPIOregs, SPI1_CLK);
		Set_PinFunction(pPublicSpi->pGPIOregs, SPI1_nSS);
		Set_PinFunction(pPublicSpi->pGPIOregs, SPI1_MISO);
		Set_PinFunction(pPublicSpi->pGPIOregs, SPI1_MOSI); 
             Set_PinPullUD(pPublicSpi->pGPIOregs, SPI1_CLK, sgip_PULL_DISABLE);
             Set_PinPullUD(pPublicSpi->pGPIOregs, SPI1_nSS, sgip_PULL_DISABLE);
             Set_PinPullUD(pPublicSpi->pGPIOregs, SPI1_MISO, sgip_PULL_DISABLE);
             Set_PinPullUD(pPublicSpi->pGPIOregs, SPI1_MOSI, sgip_PULL_DISABLE); 
         	DBGMSG(SPI_INFO,(TEXT("++[SPI] SPI_GPIO GPB_DRV_SR =%x, GPB_PUD = %x, GPB_CON = %x\r\n")
			, pPublicSpi->pGPIOregs->GPB.GP_DRV_SR
			, pPublicSpi->pGPIOregs->GPB.GP_PUD
			, pPublicSpi->pGPIOregs->GPB.GP_CON)); 
		// Clock On
#if (SPI_CLOCK == EPLL_CLOCK)
		pPublicSpi->pSYSCONregs->CLK_SRC.CLK_SRC5      = (pPublicSpi->pSYSCONregs->CLK_SRC.CLK_SRC5 & ~(0xF << 4)) | (0x7 << 4); 
		pPublicSpi->pSYSCONregs->CLK_SRC.CLK_SRC_MASK0 = (pPublicSpi->pSYSCONregs->CLK_SRC.CLK_SRC_MASK0 | (1 << 17));
		//pPublicSpi->pSYSCONregs->CLK_DIV.CLK_DIV5      = (pPublicSpi->pSYSCONregs->CLK_DIV.CLK_DIV5 & ~(0xF << 4));
		pPublicSpi->pSYSCONregs->CLK_DIV.CLK_DIV5      = (pPublicSpi->pSYSCONregs->CLK_DIV.CLK_DIV5 & ~(0xF << 4))| (2 << 4);//asdf 
#elif (SPI_CLOCK == MPLL_CLOCK)
		pPublicSpi->pSYSCONregs->CLK_SRC.CLK_SRC5      = (pPublicSpi->pSYSCONregs->CLK_SRC.CLK_SRC5 & ~(0xF << 4)) | (0x6 << 4); 
		pPublicSpi->pSYSCONregs->CLK_SRC.CLK_SRC_MASK0 = (pPublicSpi->pSYSCONregs->CLK_SRC.CLK_SRC_MASK0 | (1 << 17));
		//pPublicSpi->pSYSCONregs->CLK_DIV.CLK_DIV5      = (pPublicSpi->pSYSCONregs->CLK_DIV.CLK_DIV5 & ~(0xF << 4)) | (12 << 4);
		pPublicSpi->pSYSCONregs->CLK_DIV.CLK_DIV5      = (pPublicSpi->pSYSCONregs->CLK_DIV.CLK_DIV5 & ~(0xF << 4)) | (19 << 4);//asdf test
#endif
		pPublicSpi->pSYSCONregs->CLK_GATE.CLK_GATE_IP3 = (pPublicSpi->pSYSCONregs->CLK_GATE.CLK_GATE_IP3 & ~(0x1 << 13)) | (0x1 << 13);

	}
	else if(pPublicSpi->chnum==SPI2)
	{
		DBGMSG(SPI_INFO,(TEXT("[SPI] CH2 HW_init\r\n")));
		//Configure SPI Port Drive Strength
		pPublicSpi->pGPIOregs->GPG2.GP_DRV_SR = (pPublicSpi->pGPIOregs->GPG2.GP_DRV_SR & ~(0xFF<<0)) | (0x55<<0);

		//Set GPIO for MISO, MOSI, SPICLK, SS
		pPublicSpi->pGPIOregs->GPG2.GP_PUD= (pPublicSpi->pGPIOregs->GPG2.GP_PUD & ~(0xFF<<0));
		pPublicSpi->pGPIOregs->GPG2.GP_CON = (pPublicSpi->pGPIOregs->GPG2.GP_CON & ~(0xFFFF<<0)) | (0x3333<<0);

		// Clock On
#if (SPI_CLOCK == EPLL_CLOCK)
		pPublicSpi->pSYSCONregs->CLK_SRC.CLK_SRC5      = (pPublicSpi->pSYSCONregs->CLK_SRC.CLK_SRC5 & ~(0xF << 8)) | (0x7 << 8);
		pPublicSpi->pSYSCONregs->CLK_SRC.CLK_SRC_MASK0 = (pPublicSpi->pSYSCONregs->CLK_SRC.CLK_SRC_MASK0 | (1 << 18));
		pPublicSpi->pSYSCONregs->CLK_DIV.CLK_DIV5      = (pPublicSpi->pSYSCONregs->CLK_DIV.CLK_DIV5 & ~(0xF<<8)); 
#elif (SPI_CLOCK == MPLL_CLOCK)
		pPublicSpi->pSYSCONregs->CLK_SRC.CLK_SRC5      = (pPublicSpi->pSYSCONregs->CLK_SRC.CLK_SRC5 & ~(0xF << 8)) | (0x6 << 8); 
		pPublicSpi->pSYSCONregs->CLK_SRC.CLK_SRC_MASK0 = (pPublicSpi->pSYSCONregs->CLK_SRC.CLK_SRC_MASK0 | (1 << 18));
		pPublicSpi->pSYSCONregs->CLK_DIV.CLK_DIV5      = (pPublicSpi->pSYSCONregs->CLK_DIV.CLK_DIV5 & ~(0xF << 8)) | (13 << 8);

#endif
		pPublicSpi->pSYSCONregs->CLK_GATE.CLK_GATE_IP3 = (pPublicSpi->pSYSCONregs->CLK_GATE.CLK_GATE_IP3 & ~(0x1 << 14)) | (0x1 << 14);
	}
	else
	{
		ERRMSG((TEXT("[SPI] Invalid channel \r\n")));        
	}

	DMA_initialize_register_address((void *)pPublicSpi->pDMAC0regs,
			(void *)pPublicSpi->pDMAC1regs,
			(void *)pPublicSpi->pDMACregs,
			(void *)pPublicSpi->pSYSCONregs);

CleanUp:

	if (!bResult)
	{
		if (pPublicSpi != NULL)
		{
			if (pPublicSpi->pGPIOregs)
			{
				MmUnmapIoSpace((PVOID)pPublicSpi->pGPIOregs, sizeof(GPIO_REG));
				pPublicSpi->pGPIOregs = NULL;
			}

			if (pPublicSpi->pSPIregs)
			{
				MmUnmapIoSpace((PVOID)pPublicSpi->pSPIregs, sizeof(SPI_REG));
				pPublicSpi->pSPIregs = NULL;
			}

			if (pPublicSpi->pDMACregs)
			{
				MmUnmapIoSpace((PVOID)pPublicSpi->pDMACregs, sizeof(DMAC_REG));
				pPublicSpi->pDMACregs = NULL;
			}

			if (pPublicSpi->pDMAC0regs)
			{
				MmUnmapIoSpace((PVOID)pPublicSpi->pDMAC0regs, sizeof(DMAC_REG));
				pPublicSpi->pDMAC0regs = NULL;
			}

			if (pPublicSpi->pDMAC1regs)
			{
				MmUnmapIoSpace((PVOID)pPublicSpi->pDMAC1regs, sizeof(DMAC_REG));
				pPublicSpi->pDMAC1regs = NULL;
			}

			if (pPublicSpi->pSYSCONregs)
			{
				MmUnmapIoSpace((PVOID)pPublicSpi->pSYSCONregs, sizeof(CMU_CLK_REG));
				pPublicSpi->pSYSCONregs = NULL;
			}
		}
		bResult = FALSE;
	}

	return bResult;
}
BOOL InitializeDMABuffer(PSPI_PUBLIC_CONTEXT pPublicSpi)
{
	BOOL bResult = TRUE;

	DMA_ADAPTER_OBJECT Adapter1, Adapter2;

	DBGMSG(SPI_FUNC,(TEXT("+[SPI] InitializeBuffer\n")));

	if ( !pPublicSpi )
	{
		bResult = FALSE;
		goto CleanUp;
	}

	memset(&Adapter1, 0, sizeof(DMA_ADAPTER_OBJECT));
	Adapter1.InterfaceType = Internal;
	Adapter1.ObjectSize = sizeof(DMA_ADAPTER_OBJECT);

	memset(&Adapter2, 0, sizeof(DMA_ADAPTER_OBJECT));
	Adapter2.InterfaceType = Internal;
	Adapter2.ObjectSize = sizeof(DMA_ADAPTER_OBJECT);

	// Allocate a block of virtual memory (physically contiguous) for the DMA buffers.
	//
	pPublicSpi->SPIDMAInfo.pVirtDmaDstBufferAddr = (PBYTE)HalAllocateCommonBuffer(&Adapter1, SPI_DMA_BUF_SIZE, &pPublicSpi->SPIDMAInfo.PhysDmaDstBufferAddr, FALSE);
	DBGMSG(FALSE, (TEXT("[SPIDD] InitializeBuffer() - pVirtDmaDstBufferAddr %x\r\n"),pPublicSpi->SPIDMAInfo.pVirtDmaDstBufferAddr));
	if (pPublicSpi->SPIDMAInfo.pVirtDmaDstBufferAddr == NULL)
	{
		ERRMSG( (TEXT("[SPI] InitializeBuffer() - Failed to allocate DMA buffer for SPI.\r\n")));
		HalFreeCommonBuffer(0, 0, pPublicSpi->SPIDMAInfo.PhysDmaDstBufferAddr, pPublicSpi->SPIDMAInfo.pVirtDmaDstBufferAddr, FALSE);
		bResult = FALSE;
		goto CleanUp;
	}

	pPublicSpi->SPIDMAInfo.pVirtDmaSrcBufferAddr = (PBYTE)HalAllocateCommonBuffer(&Adapter2, SPI_DMA_BUF_SIZE, &pPublicSpi->SPIDMAInfo.PhysDmaSrcBufferAddr, FALSE);
	DBGMSG(FALSE, (TEXT("[SPIDD] InitializeBuffer() - pVirtDmaSrcBufferAddr %x\r\n"),pPublicSpi->SPIDMAInfo.pVirtDmaSrcBufferAddr));
	if (pPublicSpi->SPIDMAInfo.pVirtDmaSrcBufferAddr == NULL)
	{
		ERRMSG( (TEXT("[SPI] InitializeBuffer() - Failed to allocate DMA buffer for SPI.\r\n")));
		HalFreeCommonBuffer(0, 0, pPublicSpi->SPIDMAInfo.PhysDmaSrcBufferAddr, pPublicSpi->SPIDMAInfo.pVirtDmaSrcBufferAddr, FALSE);
		bResult = FALSE;
		goto CleanUp;
	}

	//DMA Address
	pPublicSpi->SPIDMAInfo.DmaDstAddress = (UINT)(pPublicSpi->SPIDMAInfo.PhysDmaDstBufferAddr.LowPart);
	pPublicSpi->SPIDMAInfo.DmaSrcAddress = (UINT)(pPublicSpi->SPIDMAInfo.PhysDmaSrcBufferAddr.LowPart);

	DBGMSG(SPI_INFO, (TEXT("[SPI] pVirtDmaSrcBufferAddr 0x%x   DmaSrcAddress 0x%x \r\n"),pPublicSpi->SPIDMAInfo.pVirtDmaSrcBufferAddr, pPublicSpi->SPIDMAInfo.DmaSrcAddress));
	DBGMSG(SPI_INFO, (TEXT("[SPI] pVirtDmaDstBufferAddr 0x%x   DmaDstAddress 0x%x \r\n"),pPublicSpi->SPIDMAInfo.pVirtDmaDstBufferAddr, pPublicSpi->SPIDMAInfo.DmaDstAddress));

	DBGMSG(SPI_FUNC,(TEXT("-[SPI] InitializeBuffer\n")));

CleanUp:
	return bResult;
}

PSPI_PUBLIC_CONTEXT SPI_Init(PVOID Context)
{
	PSPI_PUBLIC_CONTEXT     pPublicSpi = NULL;
	LPTSTR ActivePath = (LPTSTR) Context;
	BOOL                             bResult = TRUE;
	DWORD                          dwHwIntr=0;
	HKEY hKey = NULL;
	DDKISRINFO isri;
	DWORD dwDataSize=4;


	DBGMSG(SPI_INFO,(TEXT("*************************************************\r\n")));
	DBGMSG(SPI_INFO,(TEXT("* %a_%a : %s\r\n"),VERSION_INFO,DateInformation, ActivePath));
	DBGMSG(SPI_INFO,(TEXT("*************************************************\r\n")));  
	DBGMSG(SPI_FUNC,(TEXT("[SPI] ++HSP_Init \r\n")));

    pPublicSpi = (PSPI_PUBLIC_CONTEXT)LocalAlloc( LPTR, sizeof(SPI_PUBLIC_CONTEXT) );
	if (pPublicSpi==NULL)
	{
		ERRMSG((TEXT("[SPI] Can't not allocate for SPI Context\n")));
             LocalFree(pPublicSpi);
		bResult = FALSE;
		goto CleanUp;
	}

	pPublicSpi->hSpiEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

	pPublicSpi->dwSpiSysIntr = SYSINTR_NOP;
	hKey = OpenDeviceKey((LPCTSTR)Context);
	if (hKey == INVALID_HANDLE_VALUE ) 
	{
		ERRMSG((TEXT("[SPI] Failed to open device key\r\n")));
		bResult = FALSE;
		goto CleanUp;      
	}    

	// Read ISR information
	isri.cbSize = sizeof(isri);
	if (DDKReg_GetIsrInfo(hKey, &isri) != ERROR_SUCCESS) 
	{
		ERRMSG((TEXT("[SPI] Error getting ISR information\r\n")));
		bResult = FALSE;
		goto CleanUp;
	}

	DBGMSG(SPI_INFO,(TEXT("[SPI] IRQ(%d)\r\n"), isri.dwIrq));
	if (isri.dwSysintr == SYSINTR_NOP) 
	{
		if (!KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR, &(isri.dwIrq), sizeof(DWORD), &pPublicSpi->dwSpiSysIntr, sizeof(DWORD), NULL))
		{
			ERRMSG((TEXT("[SPI] Error KernelIoControl\r\n")));
			bResult = FALSE;
			goto CleanUp;
		}
	}

	// Get Device Index. 
	if(RegQueryValueEx(hKey, PC_REG_SPI_DEVICE_INDEX, NULL, NULL,(LPBYTE)(&pPublicSpi->chnum),&dwDataSize)!=ERROR_SUCCESS)
	{
		ERRMSG((TEXT("[SPI] Error RegQueryValueEx\r\n")));
		pPublicSpi->chnum=0;
		bResult = FALSE;
		goto CleanUp;        
	}

	DBGMSG(SPI_DEBUG,(TEXT("[SPI] chnum=%d\r\n"), pPublicSpi->chnum));

	if (!InterruptInitialize(pPublicSpi->dwSpiSysIntr, pPublicSpi->hSpiEvent, NULL, 0))
	{
		ERRMSG((TEXT("[SPI] SPI Interrupt Initialization failed!!!\n")));
		bResult = FALSE;
		goto CleanUp;
	}

	if(!HW_Init(pPublicSpi))
	{
		ERRMSG((TEXT("[SPI] HW_Init is failed\n")));
		bResult = FALSE;
		goto CleanUp;
	}

	if(!InitializeDMABuffer(pPublicSpi))
	{
		ERRMSG((TEXT("[SPI] InitializeBuffer is failed\n")));
		bResult = FALSE;
		goto CleanUp;
	}

	if(pPublicSpi->chnum==SPI0)
	{
		DBGMSG(SPI_INFO,(TEXT("[SPI] CH0 DMA \n")));
		pPublicSpi->dw_spi_dma_tx_ch = DMAsrc_SPI_0_TX;
		pPublicSpi->dw_spi_dma_rx_ch = DMAsrc_SPI_0_RX;            
	}
	else if(pPublicSpi->chnum==SPI1)
	{
		DBGMSG(SPI_INFO,(TEXT("[SPI] CH1 DMA \n")));
		pPublicSpi->dw_spi_dma_tx_ch = DMAsrc_SPI_1_TX;
		pPublicSpi->dw_spi_dma_rx_ch = DMAsrc_SPI_1_RX;            
	}
	else if(pPublicSpi->chnum==SPI2)
	{
		DBGMSG(SPI_INFO,(TEXT("[SPI] CH2 DMA \n")));        
		pPublicSpi->dw_spi_dma_tx_ch = DMAsrc_SPI_2_TX;
		pPublicSpi->dw_spi_dma_rx_ch = DMAsrc_SPI_2_RX;            
	}    
	else
	{
		ERRMSG((TEXT("[SPI] Invalid channel  \n")));
	}

	if( DMA_request_channel(&pPublicSpi->SPIDMAInfo.g_OutputDMA, pPublicSpi->dw_spi_dma_tx_ch) != TRUE )
	{
		ERRMSG((TEXT("[SPI] DMA SPI TX channel request is failed\n")));
		bResult = FALSE;
		goto CleanUp;
	}
	if( DMA_request_channel(&pPublicSpi->SPIDMAInfo.g_InputDMA, pPublicSpi->dw_spi_dma_rx_ch) != TRUE )
	{
		ERRMSG((TEXT("[SPI] DMA SPI RX channel request is failed\n")));
		bResult = FALSE;
		goto CleanUp;
	}      

	do
	{
		InitializeCriticalSection(&(pPublicSpi->CsRxAccess));
		InitializeCriticalSection(&(pPublicSpi->CsTxAccess));

		//Rx Thread
		pPublicSpi->hRxEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
		if (pPublicSpi->hRxEvent == NULL)
		{
			ERRMSG((TEXT("[SPI] SPI Rx Event creation error!!!\n")));
			bResult = FALSE;
			break;
		}

		pPublicSpi->hRxThread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)ThreadForRx, (LPVOID)pPublicSpi, 0, (LPDWORD)&pPublicSpi->dwRxThreadId);
		if (pPublicSpi->hRxThread == NULL)
		{
			ERRMSG((TEXT("[SPI] SPI Rx Thread creation error!!!\n")));
			bResult = FALSE;
			break;
		}

		pPublicSpi->hRxDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
		if (pPublicSpi->hRxDoneEvent == NULL)
		{
			ERRMSG( (TEXT("[SPI] SPI Rx Done Event creation error!!!\n")));
			bResult = FALSE;
			break;
		}

		pPublicSpi->hRxIntrDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
		if (pPublicSpi->hRxIntrDoneEvent == NULL)
		{
			ERRMSG( (TEXT("[SPI] SPI Rx Interrupt Event creation error!!!\n")));
			bResult = FALSE;
			break;
		}

		//Tx Thread
		pPublicSpi->hTxEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
		if (pPublicSpi->hTxEvent == NULL)
		{
			ERRMSG((TEXT("[SPI] SPI Tx Event creation error!!!\n")));
			bResult = FALSE;
			break;
		}

		pPublicSpi->hTxThread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)ThreadForTx, (LPVOID)pPublicSpi, 0, (LPDWORD)&pPublicSpi->dwTxThreadId);
		if (pPublicSpi->hTxThread == NULL)
		{
			ERRMSG((TEXT("[SPI] SPI Tx Thread creation error!!!\n")));
			bResult = FALSE;
			break;
		}

		pPublicSpi->hTxDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
		if (pPublicSpi->hTxDoneEvent == NULL)
		{
			ERRMSG( (TEXT("[SPI] SPI Tx Done Event creation error!!!\n")));
			bResult = FALSE;
			break;
		}

		pPublicSpi->hTxIntrDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
		if (pPublicSpi->hTxIntrDoneEvent == NULL)
		{
			ERRMSG( (TEXT("[SPI] SPI Tx Interrupt Event creation error!!!\n")));
			bResult = FALSE;
			break;
		}

		pPublicSpi->hSpiThread = CreateThread(0, 0, (LPTHREAD_START_ROUTINE)ThreadForSpi, (LPVOID)pPublicSpi, 0, (LPDWORD)&pPublicSpi->dwSpiThreadId);
		if (pPublicSpi->hSpiThread == NULL)
		{
			ERRMSG((TEXT("[SPI] SPI ISR Thread creation error!!!\n")));
			bResult = FALSE;
			break;
		}

		//Tx DMA Done ISR
		pPublicSpi->dwTxDmaDoneSysIntr = SYSINTR_NOP;
		dwHwIntr = pPublicSpi->SPIDMAInfo.g_OutputDMA.dwIRQ;
		pPublicSpi->hTxDmaDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

		if (!KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR, &dwHwIntr, sizeof(DWORD), &pPublicSpi->dwTxDmaDoneSysIntr, sizeof(DWORD), NULL))
		{
			ERRMSG((TEXT("[SPI] Failed to request the SPI_DMA sysintr.\n")));
			pPublicSpi->dwTxDmaDoneSysIntr = SYSINTR_UNDEFINED;
			bResult = FALSE;
			break;
		}

		if (!InterruptInitialize(pPublicSpi->dwTxDmaDoneSysIntr, pPublicSpi->hTxDmaDoneEvent, NULL, 0))
		{
			ERRMSG((TEXT("[SPI] DMA Interrupt Initialization failed!!!\n")));
			bResult = FALSE;
			break;
		}

		//Rx DMA Done ISR
		pPublicSpi->dwRxDmaDoneSysIntr = SYSINTR_NOP;
		dwHwIntr = pPublicSpi->SPIDMAInfo.g_InputDMA.dwIRQ;
		pPublicSpi->hRxDmaDoneEvent = CreateEvent(NULL, FALSE, FALSE, NULL);

		if (!KernelIoControl(IOCTL_HAL_REQUEST_SYSINTR, &dwHwIntr, sizeof(DWORD), &pPublicSpi->dwRxDmaDoneSysIntr, sizeof(DWORD), NULL))
		{
			ERRMSG((TEXT("[SPI] Failed to request the SPI_DMA sysintr.\n")));
			pPublicSpi->dwRxDmaDoneSysIntr = SYSINTR_UNDEFINED;
			bResult = FALSE;
			break;
		}

		if (!InterruptInitialize(pPublicSpi->dwRxDmaDoneSysIntr, pPublicSpi->hRxDmaDoneEvent, NULL, 0))
		{
			ERRMSG((TEXT("[SPI] DMA Interrupt Initialization failed!!!\n")));
			bResult = FALSE;
			break;
		}
		pPublicSpi->m_hPowerCon = CreateFile( L"PWC0:", GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, 0);
		if(pPublicSpi->m_hPowerCon == INVALID_HANDLE_VALUE)
		{
			ERRMSG((L"[SPI:ERR] Initialize() : CreateFile() m_hPowerCon Open is failed\n\r"));
			bResult = FALSE;
			break;
		}
		DMA_initialize_channel(&pPublicSpi->SPIDMAInfo.g_OutputDMA, TRUE);
		DMA_initialize_channel(&pPublicSpi->SPIDMAInfo.g_InputDMA, TRUE);
	} while (0);


CleanUp:
	DBGMSG(SPI_INIT,(TEXT("--[SPI] HSP_Init Function\r\n")));
	if(bResult)
	{
		return pPublicSpi;
	}
	else
	{
		return NULL;
	}
}

DWORD
SPI_Open(
		DWORD        pContext,
		DWORD        AccessCode,
		DWORD        ShareMode)
{
	PSPI_PUBLIC_CONTEXT         pSpiPublic  = NULL;
	PSPI_PRIVATE_CONTEXT        pSpiPrivate = NULL;
	BOOL                        bResult     = TRUE;

	DBGMSG(SPI_FUNC,(TEXT("++[SPI] SPI_Open CH\r\n")));
     	
	if(pContext != 0)
	{
		pSpiPublic  = (PSPI_PUBLIC_CONTEXT) pContext;
	}
	else
	{
		bResult = FALSE;
		goto CleanUp;
	}

	if ( !(pSpiPrivate = (PSPI_PRIVATE_CONTEXT)LocalAlloc( LPTR, sizeof(SPI_PRIVATE_CONTEXT) )) )
	{
		ERRMSG((TEXT("[SPI] Can't not allocate for SPI Context\n")));
		bResult = FALSE;
		goto CleanUp;
	}

	pSpiPrivate->State      = STATE_INIT;
	pSpiPrivate->pSpiPublic = pSpiPublic;

        // Display driver use GPB4,5,6,7(SPI1) as output mode 
        // To test SPI1, reconfiguration 
        // Apply this at Display driver 
      /*if(pSpiPublic->chnum==SPI1)
      {
		Set_PinFunction(pSpiPublic->pGPIOregs, SPI1_CLK);
		Set_PinFunction(pSpiPublic->pGPIOregs, SPI1_nSS);
		Set_PinFunction(pSpiPublic->pGPIOregs, SPI1_MISO);
		Set_PinFunction(pSpiPublic->pGPIOregs, SPI1_MOSI); 
             Set_PinPullUD(pSpiPublic->pGPIOregs, SPI1_CLK, sgip_PULL_DISABLE);
             Set_PinPullUD(pSpiPublic->pGPIOregs, SPI1_nSS, sgip_PULL_DISABLE);
             Set_PinPullUD(pSpiPublic->pGPIOregs, SPI1_MISO, sgip_PULL_DISABLE);
             Set_PinPullUD(pSpiPublic->pGPIOregs, SPI1_MOSI, sgip_PULL_DISABLE);        
      }*/
      DBGMSG(SPI_INFO,(TEXT("[SPI_CH%d] GPB_CON = %x\r\n"),pSpiPublic->chnum ,pSpiPublic->pGPIOregs->GPB.GP_CON));
	pSpiPrivate->bUseRxDMA  = FALSE;
	pSpiPrivate->bUseRxIntr = FALSE;
	pSpiPrivate->bUseTxDMA  = FALSE;
	pSpiPrivate->bUseTxIntr = FALSE;
	pSpiPrivate->spi_tx_data_addr = BASE_REG_PA_SPI0+(pSpiPublic->chnum*BASE_REG_SPI_OFFSET)+0x18;
	pSpiPrivate->spi_rx_data_addr = BASE_REG_PA_SPI0+(pSpiPublic->chnum*BASE_REG_SPI_OFFSET)+0x1C;   

CleanUp:
	DBGMSG(SPI_FUNC,(TEXT("--[SPI] SPI_Open\r\n")));
	if(bResult)
	{
		return (DWORD) pSpiPrivate;
	}
	else
	{
		return (DWORD) NULL;
	}
}

DWORD
SPI_Read(
		DWORD     hOpenContext,
		LPVOID    pBuffer,
		DWORD     Count)
{
	PSPI_PRIVATE_CONTEXT pSpiPrivate    = (PSPI_PRIVATE_CONTEXT)hOpenContext;
	PSPI_PUBLIC_CONTEXT  pSpiPublic     = pSpiPrivate->pSpiPublic;
	BOOL                 bResult        = TRUE;
	ULONG                BytesRead      = 0;
	UINT i;

	DBGMSG(SPI_FUNC,(TEXT("++[SPI] SPI_Read \r\n")));
	DBGMSG(SPI_INFO,(TEXT("++[SPI] SPI_GPIO GPB_DRV_SR =%x, GPB_PUD = %x, GPB_CON = %x\r\n")
				, pSpiPublic->pGPIOregs->GPB.GP_DRV_SR
				, pSpiPublic->pGPIOregs->GPB.GP_PUD
				, pSpiPublic->pGPIOregs->GPB.GP_CON)); 
	DBGMSG(SPI_INFO,(TEXT("++[SPI] SPI_GPIO GPG2_DRV_SR =%x, GPG2_PUD = %x, GPG2_CON = %x\r\n")
				, pSpiPublic->pGPIOregs->GPG2.GP_DRV_SR
				, pSpiPublic->pGPIOregs->GPG2.GP_PUD
				, pSpiPublic->pGPIOregs->GPG2.GP_CON));    

	if((PSPI_PRIVATE_CONTEXT)hOpenContext == NULL)
	{
		bResult = FALSE;
		goto CleanUp;
	}

	//param check
	if(pSpiPrivate->State != STATE_IDLE)
	{
		ERRMSG((TEXT("[SPI] READ ERROR : STATE IS NOT IDLE\n")));
		bResult = FALSE;
		goto CleanUp;
	}

	DBGMSG(SPI_INFO,(TEXT("[SPI] pRxBuffer : 0x%X, Count : %d\n"), pBuffer, Count));
	if(SPI_DEBUG)
	{
		memset(pSpiPublic->SPIDMAInfo.pVirtDmaDstBufferAddr, 0, SPI_DMA_BUF_SIZE);
		for (i = 0; i < Count; i++)
		{
			DBGMSG(SPI_USR2,(TEXT("%d "),pSpiPublic->SPIDMAInfo.pVirtDmaDstBufferAddr[i]));
		}
	}

	if(pSpiPrivate->bUseRxDMA)
	{
		pSpiPrivate->pRxBuffer         = pSpiPublic->SPIDMAInfo.pVirtDmaDstBufferAddr;
		pSpiPrivate->pRxDMABuffer     = (LPVOID)pSpiPublic->SPIDMAInfo.DmaDstAddress;
	}
	else
	{
		pSpiPrivate->pRxBuffer = pBuffer;
	}

	pSpiPrivate->dwRxCount = Count;
	pSpiPublic->pSpiPrivate = pSpiPrivate;

#ifdef SLAVE_READY
	if(pSpiPrivate->dwMode == SPI_SLAVE_MODE && pSpiPrivate->ClrSlaveReady) 
	{
		pSpiPrivate->ClrSlaveReady(pSpiPrivate);
	}
#endif

	SetEvent(pSpiPublic->hRxEvent);

	//Thread call

	WaitForSingleObject(pSpiPublic->hRxDoneEvent, INFINITE);
	pSpiPrivate->State = STATE_IDLE;

	if(pSpiPrivate->bUseRxDMA)
	{
		memcpy(pBuffer, pSpiPublic->SPIDMAInfo.pVirtDmaDstBufferAddr,Count);
	}
	else if(SPI_DEBUG)
	{
		memcpy(pSpiPublic->SPIDMAInfo.pVirtDmaDstBufferAddr, pBuffer,Count);
	}   

CleanUp:
	BytesRead = Count - pSpiPrivate->dwRxCount;
	DBGMSG(SPI_MSG,(TEXT("[SPI] SPI_Read : Return Value : %d\n\n"), BytesRead));
	if(SPI_DEBUG)
	{	
		for (i = 0; i < Count; i++)
		{
			DBGMSG(SPI_USR4,(TEXT("%d "),pSpiPublic->SPIDMAInfo.pVirtDmaDstBufferAddr[i]));
		}
	}	
	DBGMSG(SPI_FUNC,(TEXT("--[SPI] SPI_Read\r\n")));
	if(bResult)
	{
		return BytesRead;
	}
	else
	{
		return -1;
	}
}

DWORD
SPI_Write(
		DWORD     hOpenContext,
		LPVOID    pBuffer,
		DWORD     Count)
{
	PSPI_PRIVATE_CONTEXT pSpiPrivate    = (PSPI_PRIVATE_CONTEXT)hOpenContext;
	PSPI_PUBLIC_CONTEXT  pSpiPublic     = pSpiPrivate->pSpiPublic;
	BOOL                 bResult        = TRUE;
	ULONG                BytesWritten   = 0;
	UINT i;

	DBGMSG(SPI_FUNC,(TEXT("++[SPI] SPI_Write chnum = %d\r\n"),pSpiPublic->chnum));
	DBGMSG(SPI_INFO,(TEXT("++[SPI] SPI_GPIO GP_DRV_SR =%x, GP_PUD = %x, GP_CON = %x\r\n")
				, pSpiPublic->pGPIOregs->GPB.GP_DRV_SR
				, pSpiPublic->pGPIOregs->GPB.GP_PUD
				, pSpiPublic->pGPIOregs->GPB.GP_CON)); 

	if((PSPI_PRIVATE_CONTEXT)hOpenContext == NULL)
	{
		bResult = FALSE;
		goto CleanUp;
	}

	//param check
	if(pSpiPrivate->State != STATE_IDLE)
	{
		ERRMSG((TEXT("[SPI] WRITE ERROR : STATE IS NOT IDLE\n")));
		bResult = FALSE;
		goto CleanUp;
	}
	DBGMSG(SPI_MSG,(TEXT("[SPI] pBuffer : 0x%X, Count : %d\n"), pBuffer, Count));
	if(pSpiPrivate->bUseTxDMA)
	{
		memcpy(pSpiPublic->SPIDMAInfo.pVirtDmaSrcBufferAddr,pBuffer, Count);
		pSpiPrivate->pTxBuffer         = pSpiPublic->SPIDMAInfo.pVirtDmaSrcBufferAddr;
		pSpiPrivate->pTxDMABuffer     = (LPVOID)pSpiPublic->SPIDMAInfo.DmaSrcAddress;
	}
	else if(SPI_DEBUG)
	{
		memcpy(pSpiPublic->SPIDMAInfo.pVirtDmaSrcBufferAddr,pBuffer, Count);    
		pSpiPrivate->pTxBuffer = (LPVOID)pBuffer;
	}


	if(SPI_DEBUG)
	{
		for (i = 0; i < Count; i++)
		{
			DBGMSG(SPI_USR1,(TEXT("%d "),pSpiPublic->SPIDMAInfo.pVirtDmaSrcBufferAddr[i]));
		}
	}

	pSpiPrivate->dwTxCount = Count;
	pSpiPublic->pSpiPrivate = pSpiPrivate;
#ifdef SLAVE_READY
	if(pSpiPrivate->dwMode == SPI_MASTER_MODE && pSpiPrivate->GetSlaveReady) 
	{
		while(!(pSpiPrivate->GetSlaveReady(pSpiPrivate)))
		{
			Sleep(1);  //Do nothing 
		}

	}
#endif		
	SetEvent(pSpiPublic->hTxEvent);

	//Thread call

	WaitForSingleObject(pSpiPublic->hTxDoneEvent, INFINITE);
	pSpiPrivate->State = STATE_IDLE;

CleanUp:
	BytesWritten = Count - pSpiPrivate->dwTxCount;
	DBGMSG(SPI_INFO,(TEXT("[SPI] SPI_Write : Return Value : %d\n"), BytesWritten));

	if(SPI_DEBUG)
	{
		for (i = 0; i < Count; i++)
		{
			DBGMSG(SPI_USR3,(TEXT("%d "),pSpiPublic->SPIDMAInfo.pVirtDmaSrcBufferAddr[i]));
		}
	}

	DBGMSG(SPI_FUNC,(TEXT("--[SPI] SPI_Write\r\n")));
	if(bResult)
	{
		return BytesWritten;
	}
	else
	{
		return -1;
	}
}

BOOL
SPI_IOControl(
		DWORD dwInst,
		DWORD dwIoControlCode,
		PBYTE lpInBuf,
		DWORD nInBufSize,
		PBYTE lpOutBuf,
		DWORD nOutBufSize,
		LPDWORD lpBytesRetruned)
{
	PSPI_PRIVATE_CONTEXT pSpiPrivate = (PSPI_PRIVATE_CONTEXT)dwInst;
	PSPI_PUBLIC_CONTEXT  pSpiPublic  = pSpiPrivate->pSpiPublic;
	volatile SPI_REG    *pSPIregs   = pSpiPublic->pSPIregs;
	volatile SPI_REG    *pRxSPIregs = &pSpiPrivate->RxSPIregs;
	volatile SPI_REG    *pTxSPIregs = &pSpiPrivate->TxSPIregs;

	PSPI_SET_CONFIG             pSetConfig;
	BOOL                    bResult = TRUE;
#ifdef BSP_USEDVFS    
	DWORD Profile;
	DWORD dwBytes;
#endif 
#ifdef SPI_FULL_DUPLEX	
	DWORD dwCount=0;	
#endif 

	if((PSPI_PRIVATE_CONTEXT)dwInst == NULL)
	{
		bResult = FALSE;
		goto CleanUp;
	}

	switch(dwIoControlCode)
	{
		case SPI_IOCTL_SET_CONFIG:
			DBGMSG(SPI_INFO,(TEXT("[SPI] SPI STATE : SPI_IOCTL_SET_CONFIG\n")));
			if( nInBufSize != sizeof(SPI_SET_CONFIG) )
			{
				bResult = FALSE;
				break;
			}
			pSetConfig = (PSPI_SET_CONFIG) lpInBuf;

			pSpiPrivate->dwTimeOutVal       = pSetConfig->dwTimeOutVal;
			pSpiPrivate->dwMode             = pSetConfig->dwMode;
			pSpiPrivate->dwPrescaler        = pSetConfig->dwPrescaler;

			pSpiPrivate->bUseRxDMA          = pSetConfig->bUseRxDMA;
			pSpiPrivate->bUseRxIntr         = pSetConfig->bUseRxIntr;

			pSpiPrivate->bUseTxDMA          = pSetConfig->bUseTxDMA;
			pSpiPrivate->bUseTxIntr         = pSetConfig->bUseTxIntr;

			DBGMSG(SPI_USR2,(TEXT("[SPI%d] pSetConfig->dwTimeOutVal = %d \n"),pSpiPublic->chnum, pSetConfig->dwTimeOutVal));
			DBGMSG(SPI_USR2,(TEXT("[SPI%d] pSetConfig->dwMode       = %d \n"),pSpiPublic->chnum, pSetConfig->dwMode));
			DBGMSG(SPI_USR2,(TEXT("[SPI%d] pSetConfig->dwPrescaler  = %d (%d) \n"),pSpiPublic->chnum, pSetConfig->dwPrescaler, pSpiPrivate->dwPrescaler ));
			DBGMSG(SPI_USR2,(TEXT("[SPI%d] pSetConfig->bUseRxDMA    = %d \n"),pSpiPublic->chnum, pSetConfig->bUseRxDMA));
			DBGMSG(SPI_USR2,(TEXT("[SPI%d] pSetConfig->bUseRxIntr   = %d \n"),pSpiPublic->chnum, pSetConfig->bUseRxIntr));
			DBGMSG(SPI_USR2,(TEXT("[SPI%d] pSetConfig->bUseTxDMA    = %d \n"),pSpiPublic->chnum, pSetConfig->bUseTxDMA));
			DBGMSG(SPI_USR2,(TEXT("[SPI%d] pSetConfig->bUseTxIntr   = %d \n"),pSpiPublic->chnum, pSetConfig->bUseTxIntr));
			DBGMSG(SPI_USR2,(TEXT("[SPI%d] pSetConfig->Format       = %d \n"),pSpiPublic->chnum, pSetConfig->Format));

			if (pSetConfig->Format == SPI_FORMAT_0)
			{   
				pRxSPIregs->CH_CFG              = CPOL_RISING|CPHA_FORMAT_A;
			}
			else if (pSetConfig->Format == SPI_FORMAT_1)
			{   
				pRxSPIregs->CH_CFG = CPOL_RISING | CPHA_FORMAT_B;
			}
			else if (pSetConfig->Format == SPI_FORMAT_2)
			{   
				pRxSPIregs->CH_CFG = CPOL_FALLING | CPHA_FORMAT_A;
			}
			else if (pSetConfig->Format == SPI_FORMAT_3)
			{   
				pRxSPIregs->CH_CFG = CPOL_FALLING | CPHA_FORMAT_B;
			}
			else
			{   
				ERRMSG((TEXT("[SPI] This SPI_FORMAT is not supported\n")));
			}

			//Slave TX output time control
			pRxSPIregs->CH_CFG              &= ~(HIGH_SPEED_MASK);

			if (pSpiPrivate->dwPrescaler == 0)
			{
				pRxSPIregs->CH_CFG              |= (HIGH_SPEED_EN);
			}

			pRxSPIregs->FB_CLK_SEL          = pSetConfig->dwFBClkSel;
			pRxSPIregs->CLK_CFG             = CLKSEL|(pSpiPrivate->dwPrescaler);
			pRxSPIregs->MODE_CFG            = MODE_DEFAULT;

			DBGMSG(SPI_USR2,(TEXT("[SPI%d] pRxSPIregs->CH_CFG     = %d \n"), pSpiPublic->chnum, pRxSPIregs->CH_CFG));
			DBGMSG(SPI_USR2,(TEXT("[SPI%d] pRxSPIregs->FB_CLK_SEL = %d \n"), pSpiPublic->chnum, pRxSPIregs->FB_CLK_SEL));
			DBGMSG(SPI_USR2,(TEXT("[SPI%d] pRxSPIregs->CLK_CFG    = %d \n"), pSpiPublic->chnum, pRxSPIregs->CLK_CFG));
			DBGMSG(SPI_USR2,(TEXT("[SPI%d] pRxSPIregs->MODE_CFG   = %d \n"), pSpiPublic->chnum, pRxSPIregs->MODE_CFG));

			pTxSPIregs->CH_CFG              &= ~(HIGH_SPEED_MASK);
			pTxSPIregs->CH_CFG              = pRxSPIregs->CH_CFG;
			pTxSPIregs->FB_CLK_SEL          = pSetConfig->dwFBClkSel;           
			pTxSPIregs->CLK_CFG             = pRxSPIregs->CLK_CFG;
			pTxSPIregs->MODE_CFG            = pRxSPIregs->MODE_CFG;

			DBGMSG(SPI_USR2,(TEXT("[SPI%d] pTxSPIregs->CH_CFG     = %d \n"), pSpiPublic->chnum, pTxSPIregs->CH_CFG));
			DBGMSG(SPI_USR2,(TEXT("[SPI%d] pTxSPIregs->FB_CLK_SEL = %d \n"), pSpiPublic->chnum, pTxSPIregs->FB_CLK_SEL));
			DBGMSG(SPI_USR2,(TEXT("[SPI%d] pTxSPIregs->CLK_CFG    = %d \n"), pSpiPublic->chnum, pTxSPIregs->CLK_CFG));
			DBGMSG(SPI_USR2,(TEXT("[SPI%d] pTxSPIregs->MODE_CFG   = %d \n"), pSpiPublic->chnum, pTxSPIregs->MODE_CFG));

                    //SPI_SFR_DUMP(pSpiPublic);
                    
			if (pSetConfig->dwLineStrength == 0)
			{       
				DBGMSG(SPI_USR2,(TEXT("[SPI%d] DRV Strength 1x \n"), pSpiPublic->chnum));
				if (pSpiPublic->chnum == SPI0)
				{
					pSpiPublic->pGPIOregs->GPB.GP_DRV_SR = (pSpiPublic->pGPIOregs->GPB.GP_DRV_SR & ~(0xFF<<0)) | (0x0<<0);                    
				}    
				else if (pSpiPublic->chnum == SPI1)
				{
					pSpiPublic->pGPIOregs->GPB.GP_DRV_SR = (pSpiPublic->pGPIOregs->GPB.GP_DRV_SR & ~(0xFF<<8)) | (0x0<<8);                    
				}                        
				else if (pSpiPublic->chnum == SPI2)
				{
					pSpiPublic->pGPIOregs->GPG2.GP_DRV_SR = (pSpiPublic->pGPIOregs->GPG2.GP_DRV_SR & ~(0xFF<<0)) | (0x0<<0);      
				}
				else
				{
					ERRMSG((TEXT("[SPI%d] It's not supported MODE\n"), pSpiPublic->chnum));
				}

			}
			else if (pSetConfig->dwLineStrength == 1)
			{       
				DBGMSG(SPI_USR2,(TEXT("[SPI%d] DRV Strength 2x \n"), pSpiPublic->chnum));
				if (pSpiPublic->chnum == SPI0)
				{
					pSpiPublic->pGPIOregs->GPB.GP_DRV_SR = (pSpiPublic->pGPIOregs->GPB.GP_DRV_SR & ~(0xFF<<0)) | (0xAA<<0);                   
				}    
				else if (pSpiPublic->chnum == SPI1)
				{
					pSpiPublic->pGPIOregs->GPB.GP_DRV_SR = (pSpiPublic->pGPIOregs->GPB.GP_DRV_SR & ~(0xFF<<8)) | (0xAA<<8);                   
				}                        
				else if (pSpiPublic->chnum == SPI2)
				{
					pSpiPublic->pGPIOregs->GPG2.GP_DRV_SR = (pSpiPublic->pGPIOregs->GPG2.GP_DRV_SR & ~(0xFF<<0)) | (0xAA<<0);     
				}
				else
				{
					ERRMSG((TEXT("[SPI%d] It's not supported MODE\n"), pSpiPublic->chnum));
				}

			}
			else if (pSetConfig->dwLineStrength == 2)
			{       
				DBGMSG(SPI_USR2,(TEXT("[SPI%d] DRV Strength 3x \n"), pSpiPublic->chnum));
				if (pSpiPublic->chnum == SPI0)
				{
					pSpiPublic->pGPIOregs->GPB.GP_DRV_SR = (pSpiPublic->pGPIOregs->GPB.GP_DRV_SR & ~(0xFF<<0)) | (0x55<<0);                   
				}    
				else if (pSpiPublic->chnum == SPI1)
				{
					pSpiPublic->pGPIOregs->GPB.GP_DRV_SR = (pSpiPublic->pGPIOregs->GPB.GP_DRV_SR & ~(0xFF<<8)) | (0x55<<8);                   
				}                        
				else if (pSpiPublic->chnum == SPI2)
				{
					pSpiPublic->pGPIOregs->GPG2.GP_DRV_SR = (pSpiPublic->pGPIOregs->GPG2.GP_DRV_SR & ~(0xFF<<0)) | (0x55<<0);     
				}
				else
				{
					ERRMSG((TEXT("[SPI%d] It's not supported MODE\n"), pSpiPublic->chnum));
				}

			}
			else if (pSetConfig->dwLineStrength == 3)
			{       
				DBGMSG(SPI_USR2,(TEXT("[SPI%d] DRV Strength 4x \n"), pSpiPublic->chnum));
				if (pSpiPublic->chnum == SPI0)
				{
					pSpiPublic->pGPIOregs->GPB.GP_DRV_SR = (pSpiPublic->pGPIOregs->GPB.GP_DRV_SR & ~(0xFF<<0)) | (0xFF<<0);                   
				}    
				else if (pSpiPublic->chnum == SPI1)
				{
					pSpiPublic->pGPIOregs->GPB.GP_DRV_SR = (pSpiPublic->pGPIOregs->GPB.GP_DRV_SR & ~(0xFF<<8)) | (0xFF<<8);                   
				}                        
				else if (pSpiPublic->chnum == SPI2)
				{
					pSpiPublic->pGPIOregs->GPG2.GP_DRV_SR = (pSpiPublic->pGPIOregs->GPG2.GP_DRV_SR & ~(0xFF<<0)) | (0xFF<<0);     
				}
				else
				{
					ERRMSG((TEXT("[SPI%d] LineStrength for this channel not supported \n"), pSpiPublic->chnum));
				}

			}
			else
			{
				ERRMSG((TEXT("[SPI%d] This LineStrength value is not supported\n"), pSpiPublic->chnum));
			}


			if(pSpiPrivate->dwMode == SPI_MASTER_MODE)
			{
				pRxSPIregs->CH_CFG      |= SPI_MASTER;
				pRxSPIregs->CLK_CFG     |= ENCLK_ENABLE;

				pTxSPIregs->CH_CFG      |= SPI_MASTER;
				pTxSPIregs->CLK_CFG     |= ENCLK_ENABLE;
			}
			else if(pSpiPrivate->dwMode == SPI_SLAVE_MODE)
			{
				pRxSPIregs->CH_CFG      |= SPI_SLAVE;
				pRxSPIregs->CLK_CFG     |= ENCLK_DISABLE;

				pTxSPIregs->CH_CFG      |= SPI_SLAVE;
				pTxSPIregs->CLK_CFG     |= ENCLK_DISABLE;
			}
			else
			{
				ERRMSG((TEXT("[SPI%d] It's not supported MODE\n"), pSpiPublic->chnum));
				pSpiPrivate->State = STATE_ERROR;
			}
#ifdef BSP_USEDVFS
			if(!pSpiPublic->m_bDVFSFixed)
			{
				Profile = HIGH_PERF;
				if(!DeviceIoControl(pSpiPublic->m_hPowerCon, IOCTL_DVFS_SET_PROFILE, &Profile, sizeof(DWORD), NULL, 0, &dwBytes, NULL))
				{
					ERRMSG((L"[WAV] ERROR : It's failed to fix DVFS into HIGH_PERF\r\n"));
				}
				else
					pSpiPublic->m_bDVFSFixed = TRUE;
			}  
#endif             
            #ifdef SPI_EX_GPIO_CALLBACK
			HSPCALLBACK_Init(pSpiPrivate);
            #endif 
			break;

		case SPI_IOCTL_START:
			DBGMSG(SPI_INFO,(TEXT("[SPI%d] SPI STATE : SPI_IOCTL_START\n"), pSpiPublic->chnum));
			if(pSpiPrivate->State == STATE_ERROR)
			{
				ERRMSG((TEXT("[SPI%d] SPI_IOCTL_START ERROR\n"), pSpiPublic->chnum));
				bResult = FALSE;
				break;
			}
			pSpiPrivate->State = STATE_IDLE;

			break;

		case SPI_IOCTL_STOP:
			DBGMSG(SPI_INFO,(TEXT("[SPI%d] SPI STATE : SPI_IOCTL_STOP\n"), pSpiPublic->chnum));
#ifdef BSP_USEDVFS
			if(pSpiPublic->m_bDVFSFixed)
			{
				Profile = HIGH_PERF;
				if(!DeviceIoControl(pSpiPublic->m_hPowerCon, IOCTL_DVFS_CLEAR_PROFILE, &Profile, sizeof(DWORD), NULL, 0, &dwBytes, NULL))
				{
					ERRMSG((L"[WAV] ERROR : It's failed to clear fixed DVFS value\r\n"));
				}  
				else
					pSpiPublic->m_bDVFSFixed = FALSE;
			}   
#endif             
			break;   
#ifdef SPI_EX_GPIO_CALLBACK
		case SPI_IOCTL_SET_CALLBACK:
			DBGMSG(SPI_INFO,(TEXT("[SPI%d] SPI STATE : SPI_IOCTL_SET_CALLBACK\n"), pSpiPublic->chnum));
			HSPCALLBACK_Init(pSpiPrivate);
			break;
		case SPI_IOCTL_CLR_CALLBACK:
			DBGMSG(SPI_INFO,(TEXT("[SPI%d] SPI STATE : SPI_IOCTL_CLR_CALLBACK\n"), pSpiPublic->chnum));           
			HSPCALLBACK_Deinit(pSpiPrivate);
			break;
             
		case SPI_IOCTL_IS_SLAVE_READY:
			DBGMSG(SPI_INFO,(TEXT("[SPI%d] SPI STATE : SPI_IOCTL_IS_SLAVE_READY\n"), pSpiPublic->chnum));                     
			*(BOOL *)lpOutBuf = pSpiPrivate->GetSlaveReady(pSpiPrivate);			
			break;
#endif
#ifdef SPI_COMM_STREAM_IF
		case SPI_IOCTL_SERIAL_GET_WAIT_MASK:
			DBGMSG (SPI_INFO,(TEXT("[SPI%d]  IOCTL_SERIAL_GET_WAIT_MASK\r\n"), pSpiPublic->chnum));
			if ( (nOutBufSize < sizeof(DWORD)) || (NULL == lpOutBuf) ||(NULL == lpBytesRetruned) ) 
			{
				SetLastError (ERROR_INVALID_PARAMETER);
				bResult = FALSE;
				ERRMSG((TEXT("[SPI%d] SPI_IOCTL_SERIAL_GET_WAIT_MASK: Invalid parameter\r\n"), pSpiPublic->chnum));
				break;
			}
			// Set The Wait Mask
			*(DWORD *)lpOutBuf = pSpiPrivate->dwEventMask;
			// Return the size
			*lpBytesRetruned = sizeof(DWORD);     
			break;

		case SPI_IOCTL_SERIAL_SET_WAIT_MASK:
			DBGMSG (SPI_INFO,(TEXT("[SPI%d]  SPI_IOCTL_SERIAL_SET_WAIT_MASK\r\n"), pSpiPublic->chnum));             
			if ( (nInBufSize < sizeof(DWORD)) || (NULL == lpInBuf) ) 
			{
				SetLastError (ERROR_INVALID_PARAMETER);
				bResult = FALSE;
				ERRMSG((TEXT("[SPI%d]  SPI_IOCTL_SERIAL_SET_WAIT_MASK: Invalid parameter\r\n"), pSpiPublic->chnum));
				break;
			}
			else 
			{
				DWORD dwFlagEventMask = 0;
				dwFlagEventMask = *(DWORD *)lpInBuf;
				pSpiPrivate->dwEventMask = dwFlagEventMask;
				SetEvent(pSpiPrivate->hCommEvent);
			}     
			break;

		case SPI_IOCTL_SERIAL_WAIT_ON_MASK:
			DBGMSG (SPI_INFO,(TEXT("[SPI%d]  SPI_IOCTL_SERIAL_WAIT_ON_MASK\r\n"), pSpiPublic->chnum));                              
			if ( (nOutBufSize < sizeof(DWORD)) || (NULL == lpOutBuf) ||(NULL == lpBytesRetruned) ) 
			{
				SetLastError (ERROR_INVALID_PARAMETER);
				bResult = FALSE;
				ERRMSG((TEXT("[SPI%d] SPI_IOCTL_SERIAL_WAIT_ON_MASK: Invalid parameter\r\n"), pSpiPublic->chnum));
				break;
			}

			if(pSpiPrivate->dwEventMask==0)
			{
				return (FALSE);
			}

			WaitForSingleObject(pSpiPrivate->hCommEvent, (ULONG)-1);
			*(DWORD *)lpOutBuf = pSpiPrivate->dwEventMask;
			*lpBytesRetruned = sizeof(DWORD);   
			bResult=TRUE; 
			break;
#endif //SPI_COMM_STREAM_IF

#ifdef SPI_FULL_DUPLEX
            case SPI_IOCTL_FULL_DUPLEX:
			DBGMSG (SPI_INFO,(TEXT("[SPI%d]  SPI_IOCTL_FULL_DUPLEX\r\n"), pSpiPublic->chnum));             
			if ( (nInBufSize < sizeof(DWORD)) || (NULL == lpInBuf) ) 
			{
				SetLastError (ERROR_INVALID_PARAMETER);
				bResult = FALSE;
				ERRMSG((TEXT("[SPI%d]  SPI_IOCTL_FULL_DUPLEX: Invalid parameter\r\n"), pSpiPublic->chnum));
				break;
			}
			else 
			{
                        dwCount=(nInBufSize>nOutBufSize)? (nInBufSize):(nOutBufSize);
                        *lpBytesRetruned = dwCount;
                        if(!SPI_FullDuplex(pSpiPrivate, lpInBuf, lpOutBuf, dwCount))
                            bResult = FALSE;
			}                  
                break;
#endif 

		default:
			pSpiPrivate->Error = UNDEFINED_ERROR;
			goto CleanUp;
	}

CleanUp:
	return bResult;
}


DWORD ThreadForTx(PSPI_PUBLIC_CONTEXT pSpiPublic)
{
	volatile SPI_REG    *pSPIregs  = pSpiPublic->pSPIregs;
	PSPI_PRIVATE_CONTEXT        pSpiPrivate;

	DWORD     dwTxCount=0;
	PBYTE     pTxBuffer;
	DWORD     dwOldPerm;

	PBYTE     pTestBuffer;
	DWORD     dwTestCount;

	ULONG    TimeOut;
	ULONG    TotalTimeOut;
	ULONG    WaitReturn;

	INT      TimeOutCount;

	do
	{
		WaitForSingleObject(pSpiPublic->hTxEvent, INFINITE);

		pSpiPrivate     = (PSPI_PRIVATE_CONTEXT) pSpiPublic->pSpiPrivate;

		if(pSpiPrivate->dwTxCount != 0)
		{
			dwTestCount     = dwTxCount = pSpiPrivate->dwTxCount;
		}

		dwOldPerm   = SetProcPermissions((DWORD)-1);
		pTestBuffer = pTxBuffer = (PBYTE) MapPtrToProcess(pSpiPrivate->pTxBuffer, (HANDLE) GetCurrentProcessId());

		DBGMSG(SPI_USR2,(TEXT("[SPI] pTxBuffer : 0x%X, dwTxCount : %d \r\n"), pTxBuffer, dwTxCount));

		//Reset
		pSPIregs->CH_CFG |= SW_RST;
		while(!(pSPIregs->CH_CFG & SW_RST));
		DBGMSG(SPI_USR2,(TEXT("[SPI] HS SPI reset\n")));
		pSPIregs->CH_CFG &= ~SW_RST;

		if(pSpiPrivate->bUseTxIntr)
			// INT  + TX
		{
			DBGMSG(SPI_USR2,(TEXT("[SPI] Thread for TX : USE INT \r\n")));
			pSpiPrivate->State          =  STATE_TXINTR;

			pSPIregs->CH_CFG            =  pSpiPrivate->TxSPIregs.CH_CFG;
			pSPIregs->FB_CLK_SEL        =  pSpiPrivate->TxSPIregs.FB_CLK_SEL;                   
			pSPIregs->CLK_CFG           =  pSpiPrivate->TxSPIregs.CLK_CFG;
			pSPIregs->MODE_CFG          =  pSpiPrivate->TxSPIregs.MODE_CFG|(GetTxTriggerLevel(pSpiPublic->chnum) <<5);

			DBGMSG(SPI_USR2,(TEXT("[SPI] pSPIregs->FB_CLK_SEL = %d \n"),pSPIregs->FB_CLK_SEL));

			pSPIregs->SPI_INT_EN        =  TX_FIFORDY;
			pSPIregs->PENDING_CLR_REG   =  TX_UNDERRUN_CLR|TX_OVERRUN_CLR|RX_UNDERRUN_CLR|RX_OVERRUN_CLR|TRAILING_CLR;
			pSPIregs->CH_CFG            |= TX_CH_ON;

			if(pSpiPrivate->dwMode == SPI_MASTER_MODE)
			{
				DBGMSG(SPI_USR2,(TEXT("[SPI] Thread for TX : MASTER MODE \r\n")));
				MASTER_CS_ENABLE;
			}
			else
			{
				DBGMSG(SPI_USR2,(TEXT("[SPI] Thread for TX : SLAVE MODE \r\n")));
			}

			//Timeout value setting
			TotalTimeOut = WRITE_TIME_OUT_CONSTANT + (WRITE_TIME_OUT_MULTIPLIER/1000)*dwTxCount;
			TimeOut    = TotalTimeOut;
			WaitReturn = WaitForSingleObject(pSpiPublic->hTxIntrDoneEvent, TimeOut);

			if ( WAIT_TIMEOUT == WaitReturn )
			{
				// Timeout
				ERRMSG ((TEXT("Write timeout!!!\r\n")));
				goto LEAVEWRITE;
			}

			TimeOutCount = 1000;
			while((((pSPIregs ->SPI_STATUS>>6) & 0x1ff) && !(pSPIregs ->SPI_STATUS & TX_DONE))
					|| (TimeOutCount>0))
			{
				TimeOutCount--;
			}

		}
		else if(pSpiPrivate->bUseTxDMA)
			// DMA + TX
		{
			DWORD dwDmaLen            = dwTxCount & 0xFFFFF;

			DBGMSG(SPI_USR2,(TEXT("[SPI] Thread for TX : USE DMA (TxCount : %d) \r\n"),dwDmaLen));

			pSpiPrivate->State = STATE_TXDMA;

			if(pSpiPrivate->dwMode == SPI_MASTER_MODE)
			{
				pSPIregs->CH_CFG     = pSpiPrivate->TxSPIregs.CH_CFG;
				pSPIregs->FB_CLK_SEL = pSpiPrivate->TxSPIregs.FB_CLK_SEL;                  
				pSPIregs->CLK_CFG    = pSpiPrivate->TxSPIregs.CLK_CFG;
				pSPIregs->MODE_CFG   = pSpiPrivate->TxSPIregs.MODE_CFG;
			}
			else
			{
				pSPIregs->CH_CFG     = pSpiPrivate->TxSPIregs.CH_CFG;
				pSPIregs->FB_CLK_SEL = pSpiPrivate->TxSPIregs.FB_CLK_SEL;                                   
				pSPIregs->CLK_CFG    = pSpiPrivate->TxSPIregs.CLK_CFG;
				pSPIregs->MODE_CFG   = pSpiPrivate->TxSPIregs.MODE_CFG;
			}
                     
			if(dwDmaLen > 0)
			{
				pSPIregs->MODE_CFG   |=    TX_DMA_ON|DMA_SINGLE;
				pSPIregs->CH_CFG     |=    TX_CH_ON;

				{
					DBGMSG(SPI_USR2,(TEXT("[SPI] pSpiPrivate->pTxDMABuffer : 0x%X \r\n"), pSpiPrivate->pTxDMABuffer));

					DMA_set_channel_source(&pSpiPublic->SPIDMAInfo.g_OutputDMA, (UINT)pSpiPrivate->pTxDMABuffer, BYTE_UNIT, BURST_1, INCREASE);
					DMA_set_channel_destination(&pSpiPublic->SPIDMAInfo.g_OutputDMA, pSpiPrivate->spi_tx_data_addr, BYTE_UNIT, BURST_1, FIXED);
					DMA_set_channel_transfer_size(&pSpiPublic->SPIDMAInfo.g_OutputDMA, dwDmaLen);
					DMA_initialize_LLI(&pSpiPublic->SPIDMAInfo.g_OutputDMA, 1);
					DMA_set_LLI_entry(&pSpiPublic->SPIDMAInfo.g_OutputDMA, 0, LLI_FIRST_ENTRY, (UINT)pSpiPrivate->pTxDMABuffer,
							pSpiPrivate->spi_tx_data_addr, dwDmaLen);
				}

				if(pSpiPrivate->dwMode == SPI_MASTER_MODE)
				{
					DBGMSG(SPI_USR2,(TEXT("[SPI] Thread for TX : MASTER MODE \r\n")));
					MASTER_CS_ENABLE;
				}
				else
				{
					DBGMSG(SPI_USR2,(TEXT("[SPI] Thread for TX : SLAVE MODE \r\n")));
				}
#ifdef SLAVE_READY
				if(pSpiPrivate->dwMode == SPI_MASTER_MODE && pSpiPrivate->GetSlaveReady) 
				{
					while(!(pSpiPrivate->GetSlaveReady(pSpiPrivate)))
					{
						Sleep(1) ;  //Do nothing 
					}

				}
#endif					
                        //SPI_SFR_DUMP(pSpiPublic);
				DMA_channel_start(&pSpiPublic->SPIDMAInfo.g_OutputDMA);
#ifdef SLAVE_READY
				if(pSpiPrivate->dwMode == SPI_SLAVE_MODE && pSpiPrivate->SetSlaveReady) 
				{
					//                                        while((pSPIregs->SPI_STATUS & (0x1FF >> 6)) < (GetFifoSize(pSpiPublic->chnum)))
					{
						;  //Do nothing 
					}
					pSpiPrivate->SetSlaveReady(pSpiPrivate);
				}

#endif								

				//Timeout value setting
				TotalTimeOut = WRITE_TIME_OUT_CONSTANT + (WRITE_TIME_OUT_MULTIPLIER/1000)*dwTxCount;
				TimeOut    =    TotalTimeOut;
				WaitReturn = WaitForSingleObject(pSpiPublic->hTxDmaDoneEvent, TimeOut);
				
#ifdef SLAVE_READY
				if(pSpiPrivate->dwMode == SPI_SLAVE_MODE && pSpiPrivate->ClrSlaveReady) 
				{
					pSpiPrivate->ClrSlaveReady(pSpiPrivate);
				}
#endif	
				if ( WAIT_TIMEOUT == WaitReturn )
				{
					// Timeout
					DBGMSG (TRUE, (TEXT("Write timeout!!!\r\n")));
					// Stop output DMA
					DMA_channel_stop(&pSpiPublic->SPIDMAInfo.g_OutputDMA);
					goto LEAVEWRITE;
				}

                		DMA_clear_interrupt_pending(&pSpiPublic->SPIDMAInfo.g_OutputDMA);
                		DMA_set_interrupt_mask(&pSpiPublic->SPIDMAInfo.g_OutputDMA);
                		InterruptDone(pSpiPublic->dwTxDmaDoneSysIntr);
                		DMA_clear_interrupt_mask(&pSpiPublic->SPIDMAInfo.g_OutputDMA);                

				pSpiPrivate->dwTxCount -= dwDmaLen;
				pSpiPrivate->pTxBuffer = (((PUINT) pSpiPrivate->pTxBuffer) + dwDmaLen);
			}

			TimeOutCount = 1000;
			while((((pSPIregs ->SPI_STATUS>>6) & 0x1ff) && !(pSPIregs ->SPI_STATUS & TX_DONE))
					|| (TimeOutCount>0))
			{
				TimeOutCount--;
			}
		}
		else
			// POLLING + TX
		{
			ULONG Count;
			DBGMSG(SPI_USR2,(TEXT("[SPI] Thread for TX : USE Polling (TxCount : %d) \r\n"), dwTxCount));

			pSPIregs->CH_CFG     = pSpiPrivate->TxSPIregs.CH_CFG;
			pSPIregs->CLK_CFG     = pSpiPrivate->TxSPIregs.CLK_CFG;
			pSPIregs->MODE_CFG  = pSpiPrivate->TxSPIregs.MODE_CFG;

			pSPIregs->CH_CFG         |=    TX_CH_ON;

			if(pSpiPrivate->dwMode == SPI_MASTER_MODE)
			{
				DBGMSG(SPI_USR2,(TEXT("[SPI] Thread for TX : MASTER MODE \r\n")));
				MASTER_CS_ENABLE;
			}
			else
			{
				DBGMSG(SPI_USR2,(TEXT("[SPI] Thread for TX : SLAVE MODE \r\n")));
			}
			TotalTimeOut = WRITE_TIME_OUT_CONSTANT*100*50 + (WRITE_TIME_OUT_MULTIPLIER)*dwTxCount;
			Count = 0;
			do
			{
				while(((pSPIregs ->SPI_STATUS>>6) & 0x1ff)== GetFifoSize(pSpiPublic->chnum)) //FIFO_FULL
				{
					Count++;
					if(TotalTimeOut == Count)
					{
						// Timeout
						ERRMSG ((TEXT("Write timeout!!!\r\n")));
						goto LEAVEWRITE;
					}
				}
				pSPIregs->SPI_TX_DATA = *(PBYTE)pSpiPrivate->pTxBuffer;
			} while(--pSpiPrivate->dwTxCount > 0 && ++(PBYTE)pSpiPrivate->pTxBuffer);

			TimeOutCount = 1000;
			while((((pSPIregs ->SPI_STATUS>>6) & 0x1ff) && !(pSPIregs ->SPI_STATUS & TX_DONE))
					|| (TimeOutCount>0))
			{
				TimeOutCount--;
			}
		}

LEAVEWRITE:

#ifdef TEST_MODE
		do
		{
			DBGMSG(SPI_USR2,(TEXT("[SPI] WRITE BYTE : %02X(dwTxCount : %d)\n"), *pTestBuffer, dwTestCount));
		} while( (--dwTestCount > 0) && ++pTestBuffer);
#endif

		DBGMSG(FALSE,(TEXT("[SPI] TX_CH_OFF \n")));
		pSPIregs->CH_CFG     &= ~TX_CH_ON;
		if(pSpiPrivate->dwMode == SPI_MASTER_MODE)
		{
			MASTER_CS_DISABLE;
		}

		UnMapPtr(pTxBuffer);
		SetProcPermissions(dwOldPerm);

		SetEvent(pSpiPublic->hTxDoneEvent);
	} while(TRUE);

	return 0;
}

DWORD ThreadForRx(PSPI_PUBLIC_CONTEXT pSpiPublic)
{
	volatile SPI_REG    *pSPIregs   = pSpiPublic->pSPIregs;
	PSPI_PRIVATE_CONTEXT        pSpiPrivate;

	DWORD     dwRxCount=0;
	PBYTE     pRxBuffer;
	DWORD     dwOldPerm;

	PBYTE     pTestBuffer;
	DWORD     dwTestCount;

	ULONG    TimeOut;
	ULONG    TotalTimeOut;
	ULONG    WaitReturn;

	do
	{
		WaitForSingleObject(pSpiPublic->hRxEvent, INFINITE);

		pSpiPrivate = (PSPI_PRIVATE_CONTEXT) pSpiPublic->pSpiPrivate;

		if(pSpiPrivate->dwRxCount != 0)
		{
			dwTestCount = dwRxCount = pSpiPrivate->dwRxCount;
		}

		dwOldPerm   = SetProcPermissions((DWORD)-1);
		pTestBuffer = pRxBuffer = (PBYTE) MapPtrToProcess(pSpiPrivate->pRxBuffer, (HANDLE) GetCurrentProcessId());

		DBGMSG(SPI_USR2,(TEXT("[SPI] pRxBuffer : 0x%X, dwRxCount : %d \r\n"), pRxBuffer, dwRxCount));

		//Reset
		pSPIregs->CH_CFG |= SW_RST;
		while(!(pSPIregs->CH_CFG & SW_RST));
		DBGMSG(SPI_USR2,(TEXT("[SPI] HS SPI reset\n")));
		pSPIregs->CH_CFG &= ~SW_RST;

		if(pSpiPrivate->bUseRxIntr)
			//INT Mode + RX
		{
			DBGMSG(SPI_USR2,(TEXT("[SPI] Thread for RX : USE INT \r\n")));
			pSpiPrivate->State = STATE_RXINTR;

			if(pSpiPrivate->dwMode == SPI_MASTER_MODE)
			{
				pSPIregs->CH_CFG         = pSpiPrivate->RxSPIregs.CH_CFG;
				pSPIregs->FB_CLK_SEL     = pSpiPrivate->RxSPIregs.FB_CLK_SEL;                                   
				pSPIregs->CLK_CFG        = pSpiPrivate->RxSPIregs.CLK_CFG;
				pSPIregs->MODE_CFG       = pSpiPrivate->RxSPIregs.MODE_CFG|TRAIL_CNT(0x3FF)|(RX_TRIG_LEVEL<<11);
				pSPIregs->PACKET_CNT_REG = PACKET_CNT_EN | (pSpiPrivate->dwRxCount) ;
			}
			else
			{
				pSPIregs->CH_CFG   = pSpiPrivate->RxSPIregs.CH_CFG;
				pSPIregs->FB_CLK_SEL     = pSpiPrivate->RxSPIregs.FB_CLK_SEL;                   
				pSPIregs->CLK_CFG  = pSpiPrivate->RxSPIregs.CLK_CFG;
				pSPIregs->MODE_CFG = pSpiPrivate->RxSPIregs.MODE_CFG|TRAIL_CNT(0x3FF)|(RX_TRIG_LEVEL<<11);
				pSPIregs->PACKET_CNT_REG = PACKET_CNT_EN | (pSpiPrivate->dwRxCount) ;								
			}
			DBGMSG(SPI_USR2,(TEXT("[SPI] pSPIregs->FB_CLK_SEL = %d \n"),pSPIregs->FB_CLK_SEL));
			pSPIregs->SPI_INT_EN      =  RX_FIFORDY;
			pSPIregs->PENDING_CLR_REG =  TX_UNDERRUN_CLR|TX_OVERRUN_CLR|RX_UNDERRUN_CLR|RX_OVERRUN_CLR|TRAILING_CLR;
			pSPIregs->CH_CFG          |= RX_CH_ON;

			if(pSpiPrivate->dwMode == SPI_MASTER_MODE)
			{
				DBGMSG(SPI_USR2,(TEXT("[SPI] Thread for RX : MASTER MODE \r\n")));
				MASTER_CS_ENABLE;
			}
			else
			{
				DBGMSG(SPI_USR2,(TEXT("[SPI] Thread for RX : SLAVE MODE \r\n")));
			}

			//Timeout value setting
			TotalTimeOut = READ_TIME_OUT_CONSTANT + (READ_TIME_OUT_MULTIPLIER/1000)*dwRxCount;
			TimeOut    =    TotalTimeOut;
			WaitReturn = WaitForSingleObject(pSpiPublic->hRxIntrDoneEvent, TimeOut);

			if ( WAIT_TIMEOUT == WaitReturn )
			{
				// Timeout
				ERRMSG ((TEXT("Read timeout!!!\r\n")));
				goto LEAVEREAD;
			}
		}

		else if(pSpiPrivate->bUseRxDMA)
			//DMA Mode + Rx
		{
			DWORD dwDmaLen    = (pSpiPrivate->dwRxCount & 0xFFFFF);

			DBGMSG(SPI_USR2,(TEXT("[SPI] Thread for RX : USE DMA \r\n")));

			pSpiPrivate->State = STATE_RXDMA;

			//Reset
			if(pSpiPrivate->dwMode == SPI_MASTER_MODE)
			{
				pSPIregs->CH_CFG         = pSpiPrivate->RxSPIregs.CH_CFG;
				pSPIregs->FB_CLK_SEL     = pSpiPrivate->RxSPIregs.FB_CLK_SEL;                   
				pSPIregs->CLK_CFG        = pSpiPrivate->RxSPIregs.CLK_CFG;
				pSPIregs->MODE_CFG       = pSpiPrivate->RxSPIregs.MODE_CFG;
				pSPIregs->PACKET_CNT_REG = PACKET_CNT_EN | (pSpiPrivate->dwRxCount);
			}
			else
			{
				pSPIregs->CH_CFG     = pSpiPrivate->RxSPIregs.CH_CFG;
				pSPIregs->FB_CLK_SEL     = pSpiPrivate->RxSPIregs.FB_CLK_SEL;                   
				pSPIregs->CLK_CFG      = pSpiPrivate->RxSPIregs.CLK_CFG;
				pSPIregs->MODE_CFG  = pSpiPrivate->RxSPIregs.MODE_CFG;
				pSPIregs->PACKET_CNT_REG = PACKET_CNT_EN | (pSpiPrivate->dwRxCount);								
			}
                     
			if(dwDmaLen > 0)
			{
				pSPIregs->MODE_CFG        |=    RX_DMA_ON|DMA_SINGLE;
				pSPIregs->CH_CFG         |=    RX_CH_ON;

				{

					DMA_set_channel_source(&pSpiPublic->SPIDMAInfo.g_InputDMA, pSpiPrivate->spi_rx_data_addr, BYTE_UNIT, BURST_1, FIXED);
					DMA_set_channel_destination(&pSpiPublic->SPIDMAInfo.g_InputDMA, (UINT)pSpiPrivate->pRxDMABuffer, BYTE_UNIT, BURST_1, INCREASE);
					DMA_set_channel_transfer_size(&pSpiPublic->SPIDMAInfo.g_InputDMA, dwDmaLen);
					DMA_initialize_LLI(&pSpiPublic->SPIDMAInfo.g_InputDMA, 1);
					DMA_set_LLI_entry(&pSpiPublic->SPIDMAInfo.g_InputDMA, 0, LLI_FIRST_ENTRY, pSpiPrivate->spi_rx_data_addr,
							(UINT)pSpiPrivate->pRxDMABuffer, dwDmaLen);
				}

				if(pSpiPrivate->dwMode == SPI_MASTER_MODE)
				{
					DBGMSG(SPI_USR2,(TEXT("[SPI] Thread for RX : MASTER MODE \r\n")));
					MASTER_CS_ENABLE;
				}
				else
				{
					DBGMSG(SPI_USR2,(TEXT("[SPI] Thread for RX : SLAVE MODE \r\n")));
				}
                        //SPI_SFR_DUMP(pSpiPublic);
				DMA_channel_start(&pSpiPublic->SPIDMAInfo.g_InputDMA);
#ifdef SLAVE_READY
				if(pSpiPrivate->dwMode == SPI_SLAVE_MODE && pSpiPrivate->SetSlaveReady) 
				{
					pSpiPrivate->SetSlaveReady(pSpiPrivate);
				}
#endif
				//Timeout value setting
				TotalTimeOut = READ_TIME_OUT_CONSTANT + (READ_TIME_OUT_MULTIPLIER/1000)*dwRxCount;
				TimeOut    =    TotalTimeOut;
				WaitReturn = WaitForSingleObject(pSpiPublic->hRxDmaDoneEvent, TimeOut);
#ifdef SLAVE_READY
				if(pSpiPrivate->dwMode == SPI_SLAVE_MODE && pSpiPrivate->ClrSlaveReady) 
				{
					pSpiPrivate->ClrSlaveReady(pSpiPrivate);
				}
#endif
				if ( WAIT_TIMEOUT == WaitReturn )
				{
					// Timeout
					ERRMSG ((TEXT("Read timeout!!!\r\n")));
					// Stop input DMA
					DMA_channel_stop(&pSpiPublic->SPIDMAInfo.g_InputDMA);
					goto LEAVEREAD;
				}

                            DMA_clear_interrupt_pending(&pSpiPublic->SPIDMAInfo.g_InputDMA);
                            DMA_set_interrupt_mask(&pSpiPublic->SPIDMAInfo.g_InputDMA);
                            InterruptDone(pSpiPublic->dwRxDmaDoneSysIntr);
                            DMA_clear_interrupt_mask(&pSpiPublic->SPIDMAInfo.g_InputDMA);                

				pSpiPrivate->dwRxCount -= dwDmaLen;
				pSpiPrivate->pRxBuffer = (PBYTE) (((PUINT) pSpiPrivate->pRxBuffer) + dwDmaLen);
			}
		}
		else
		{
			//POLLING Mode + RX
			ULONG Count;
			DBGMSG(SPI_USR2,(TEXT("[SPI] Thread for RX : USE Polling (RxCount : %d) \r\n"), dwRxCount));

			if(pSpiPrivate->dwMode == SPI_MASTER_MODE)
			{
				pSPIregs->CH_CFG         = pSpiPrivate->RxSPIregs.CH_CFG;
				pSPIregs->CLK_CFG        = pSpiPrivate->RxSPIregs.CLK_CFG;
				pSPIregs->MODE_CFG       = pSpiPrivate->RxSPIregs.MODE_CFG;
				pSPIregs->PACKET_CNT_REG = PACKET_CNT_EN | (pSpiPrivate->dwRxCount);
			}
			else
			{
				pSPIregs->CH_CFG     = pSpiPrivate->RxSPIregs.CH_CFG;
				pSPIregs->CLK_CFG     = pSpiPrivate->RxSPIregs.CLK_CFG;
				pSPIregs->MODE_CFG  = pSpiPrivate->RxSPIregs.MODE_CFG;
			}

			pSPIregs->CH_CFG     |= RX_CH_ON;

			if(pSpiPrivate->dwMode == SPI_MASTER_MODE)
			{
				DBGMSG(SPI_USR2,(TEXT("[SPI] Thread for RX : MASTER MODE \r\n")));
				MASTER_CS_ENABLE;
			}
			else
			{
				DBGMSG(SPI_USR2,(TEXT("[SPI] Thread for RX : SLAVE MODE \r\n")));
			}

			TotalTimeOut = READ_TIME_OUT_CONSTANT*100*50 + (READ_TIME_OUT_MULTIPLIER)*dwRxCount;
			Count = 0;
			do
			{
				while (((pSPIregs ->SPI_STATUS >> 15) & 0x1ff) == FIFO_EMPTY)
				{
					Count++;
					if(TotalTimeOut == Count)
					{
						// Timeout
						ERRMSG ((TEXT("Read timeout!!!\r\n")));
						goto LEAVEREAD;
					}
				}
				*(PBYTE)pSpiPrivate->pRxBuffer = pSPIregs->SPI_RX_DATA;
			} while(--pSpiPrivate->dwRxCount > 0 && ++(PBYTE)pSpiPrivate->pRxBuffer);
		}

LEAVEREAD:

#ifdef TEST_MODE
		do
		{
			DBGMSG(SPI_USR2,(TEXT("[SPI] READ BYTE : %02X(dwRxCount %d)\n"), *pTestBuffer, dwTestCount));
		} while((--dwTestCount > 0) && ++pTestBuffer);
#endif

		DBGMSG(FALSE,(TEXT("[SPI] RX_CH_OFF \n")));
		pSPIregs->CH_CFG     &= ~RX_CH_ON;
		if(pSpiPrivate->dwMode == SPI_MASTER_MODE)
		{
			MASTER_CS_DISABLE;
		}

		UnMapPtr(pRxBuffer);
		SetProcPermissions(dwOldPerm);

		SetEvent(pSpiPublic->hRxDoneEvent);
#ifdef SPI_COMM_STREAM_IF        
		pSpiPrivate->dwEventMask |= EV_RXCHAR;
		SetEvent(pSpiPrivate->hCommEvent);
#endif 
	} while(TRUE);

	return 0;
}

DWORD ThreadForSpi(PSPI_PUBLIC_CONTEXT pSpiPublic)
{
	volatile SPI_REG *pSPIregs  = pSpiPublic->pSPIregs;        // for HS-SPI
	PSPI_PRIVATE_CONTEXT pSpiPrivate;
	UINT     dwRxCount = 0;
	UINT     dwTxCount = 0;
	DWORD     dwOldPerm = 0;

	DBGMSG(SPI_USR3,(TEXT("[SPI] ThreadForSpi thread is created \r\n")));
	do
	{
		WaitForSingleObject(pSpiPublic->hSpiEvent, INFINITE);

		pSpiPrivate = (PSPI_PRIVATE_CONTEXT) pSpiPublic->pSpiPrivate;
		dwOldPerm = SetProcPermissions((DWORD)-1);
		if(pSpiPrivate->State == STATE_RXINTR)
		{
			if(pSpiPrivate->dwMode == SPI_MASTER_MODE)
			{
				dwRxCount = ((pSPIregs ->SPI_STATUS>>15) & 0x1ff) ;
				DBGMSG(SPI_USR3,(TEXT("MR %d, %d\r\n"),pSpiPrivate->dwRxCount, dwRxCount));                                   
				if (dwRxCount != 0)
				{
					do
					{
						//                     while (((pSPIregs ->SPI_STATUS>>15)&0x1ff)==FIFO_EMPTY);
						*(PBYTE)pSpiPrivate->pRxBuffer = pSPIregs->SPI_RX_DATA;
						DBGMSG(SPI_USR4,(TEXT("MR %d, %d\r\n"),pSpiPrivate->dwRxCount, dwRxCount));
					} while(--pSpiPrivate->dwRxCount > 0 && ++(PBYTE)pSpiPrivate->pRxBuffer && --dwRxCount > 0);
				} 
				else
				{
					ERRMSG((TEXT("dwRxCount is zero %d\r\n")));                                                           							
				}

				if(pSpiPrivate->dwRxCount ==0)
				{
					pSPIregs->SPI_INT_EN    &=    ~(RX_FIFORDY|TX_FIFORDY);
					SetEvent(pSpiPublic->hRxIntrDoneEvent);
				}
				DBGMSG(SPI_USR3,(TEXT("[SPI]MR %d, %d\r\n"),pSpiPrivate->dwRxCount, dwRxCount));
			}
			else
			{
				dwRxCount = ((pSPIregs ->SPI_STATUS>>15) & 0x1ff) ;
				DBGMSG(SPI_USR3,(TEXT("SR %d, %d\r\n"),pSpiPrivate->dwRxCount, dwRxCount));                                   
				if (dwRxCount != 0)
				{
					do
					{
						*(PBYTE)pSpiPrivate->pRxBuffer = pSPIregs->SPI_RX_DATA;
						DBGMSG(SPI_USR4,(TEXT("SR %d, %d\r\n"),pSpiPrivate->dwRxCount, dwRxCount));                   
					} while(--pSpiPrivate->dwRxCount > 0 && ++(PBYTE)pSpiPrivate->pRxBuffer && --dwRxCount > 0);
				}
				else
				{
					ERRMSG((TEXT("dwRxCount is zero %d\r\n")));                                                           							
					//                                    SetEvent(pSpiPublic->hRxIntrDoneEvent);																		
				}

				if(pSpiPrivate->dwRxCount ==0)
				{
					pSPIregs->SPI_INT_EN    &=    ~(RX_FIFORDY|TX_FIFORDY);
					SetEvent(pSpiPublic->hRxIntrDoneEvent);
				}
				DBGMSG(SPI_USR3,(TEXT("[SPI]SR %d, %d\r\n"),pSpiPrivate->dwRxCount, dwRxCount));
			}
		}

		else if(pSpiPrivate->State == STATE_TXINTR)
		{
			if(pSpiPrivate->dwMode == SPI_MASTER_MODE)
			{
				dwTxCount = GetFifoSize(pSpiPublic->chnum)-((pSPIregs ->SPI_STATUS>>6) & 0x1ff) ;
				if (dwTxCount != 0)
				{
					do
					{
						pSPIregs->SPI_TX_DATA = *(PBYTE)pSpiPrivate->pTxBuffer;
						DBGMSG(SPI_USR4,(TEXT("MT %d, %d\r\n"),pSpiPrivate->dwTxCount, dwTxCount));                                     
					} while(--pSpiPrivate->dwTxCount > 0 && ++(PBYTE)pSpiPrivate->pTxBuffer && --dwTxCount > 0);
				}
				else
				{
					ERRMSG((TEXT("dwTxCount is zero %d\r\n")));                                                           							
					//                                    SetEvent(pSpiPublic->hTxIntrDoneEvent);																		
				}

				if(pSpiPrivate->dwTxCount ==0)
				{
					pSPIregs->SPI_INT_EN    &=    ~(RX_FIFORDY|TX_FIFORDY);
					SetEvent(pSpiPublic->hTxIntrDoneEvent);
				}
				DBGMSG(SPI_USR3,(TEXT("[SPI]MT %d, %d\r\n"),pSpiPrivate->dwTxCount, dwTxCount));                                                        
			}
			else
			{
				dwTxCount = GetFifoSize(pSpiPublic->chnum) -((pSPIregs ->SPI_STATUS>>6) & 0x1ff) ;
				if (dwTxCount != 0)
				{
					do
					{
						pSPIregs->SPI_TX_DATA = *(PBYTE)pSpiPrivate->pTxBuffer;
						DBGMSG(SPI_USR4,(TEXT("ST %d, %d\r\n"),pSpiPrivate->dwTxCount, dwTxCount));                                                           
					} while(--pSpiPrivate->dwTxCount > 0 && ++(PBYTE)pSpiPrivate->pTxBuffer && --dwTxCount > 0);
				}
				else
				{
					ERRMSG((TEXT("dwTxCount is zero %d\r\n")));                                                           							
					//                                    SetEvent(pSpiPublic->hTxIntrDoneEvent);									
				}
				if(pSpiPrivate->dwTxCount ==0)
				{
					pSPIregs->SPI_INT_EN    &=    ~(RX_FIFORDY|TX_FIFORDY);
					SetEvent(pSpiPublic->hTxIntrDoneEvent);
				}
				DBGMSG(SPI_USR3,(TEXT("[SPI]ST %d, %d\r\n"),pSpiPrivate->dwTxCount, dwTxCount));                                                                        
			}
		}
		else
		{
			ERRMSG((TEXT("[SPI] UNSOLVED OPERATION\n")));
		}

		//END_POINT:
		SetProcPermissions(dwOldPerm);
		InterruptDone(pSpiPublic->dwSpiSysIntr);
	} while(TRUE);
	return 0;
}


void SPI_PowerDown (PSPI_PUBLIC_CONTEXT pPublicSpi)
{
	DBGMSG(SPI_MSG,(TEXT("[SPI] ++PowerDown()\n\r")));

	if((PSPI_PUBLIC_CONTEXT)pPublicSpi == NULL)
	{
		goto CleanUp;
	}
	DBGMSG(SPI_MSG,(TEXT("[SPI] --PowerDown()\n\r")));

CleanUp:
	return;
}

void SPI_Deinit (PSPI_PUBLIC_CONTEXT pPublicSpi)
{

	DBGMSG(SPI_INIT,(TEXT("++[SPI] SPI_Deinit\r\n")));
	if((PSPI_PUBLIC_CONTEXT)pPublicSpi == NULL)
	{
		goto CleanUp;
	}

	// Delete CS
	DeleteCriticalSection(&(pPublicSpi->CsRxAccess));
	DeleteCriticalSection(&(pPublicSpi->CsTxAccess));

	// Deinitialize Buffer
	HalFreeCommonBuffer(0, 0, pPublicSpi->SPIDMAInfo.PhysDmaDstBufferAddr, pPublicSpi->SPIDMAInfo.pVirtDmaDstBufferAddr, FALSE);
	HalFreeCommonBuffer(0, 0, pPublicSpi->SPIDMAInfo.PhysDmaSrcBufferAddr, pPublicSpi->SPIDMAInfo.pVirtDmaSrcBufferAddr, FALSE);

	// Clear value assigned to DMA physical Address
	pPublicSpi->SPIDMAInfo.DmaDstAddress = 0;
	pPublicSpi->SPIDMAInfo.DmaSrcAddress = 0;

	// DMA Channel Stop
	DMA_channel_stop(&pPublicSpi->SPIDMAInfo.g_OutputDMA);
	DMA_channel_stop(&pPublicSpi->SPIDMAInfo.g_InputDMA);
	DMA_release_channel(&pPublicSpi->SPIDMAInfo.g_OutputDMA);
	DMA_release_channel(&pPublicSpi->SPIDMAInfo.g_InputDMA);

	// Assign SYSINTR_NOP value
	pPublicSpi->dwSpiSysIntr = SYSINTR_NOP;

	// Close Handle
	if (pPublicSpi->hRxEvent != NULL)
	{
		CloseHandle(pPublicSpi->hRxEvent);
	}
	if (pPublicSpi->hRxDoneEvent != NULL)
	{
		CloseHandle(pPublicSpi->hRxDoneEvent);
	}
	if (pPublicSpi->hRxIntrDoneEvent != NULL)
	{
		CloseHandle(pPublicSpi->hRxIntrDoneEvent);
	}
	if (pPublicSpi->hTxEvent != NULL)
	{
		CloseHandle(pPublicSpi->hTxEvent);
	}
	if (pPublicSpi->hTxDoneEvent != NULL)
	{
		CloseHandle(pPublicSpi->hTxDoneEvent);
	}
	if (pPublicSpi->hTxIntrDoneEvent != NULL)
	{
		CloseHandle(pPublicSpi->hTxIntrDoneEvent);
	}
	if (pPublicSpi->hSpiEvent != NULL)
	{
		CloseHandle(pPublicSpi->hSpiEvent);
	}
	if (pPublicSpi->hTxDmaDoneEvent != NULL)
	{
		CloseHandle(pPublicSpi->hTxDmaDoneEvent);
	}
	if (pPublicSpi->hRxDmaDoneEvent != NULL)
	{
		CloseHandle(pPublicSpi->hRxDmaDoneEvent);
	}
	if (pPublicSpi->hRxThread != NULL)
	{
		CloseHandle(pPublicSpi->hRxThread);
	}
	if (pPublicSpi->hTxThread != NULL)
	{
		CloseHandle(pPublicSpi->hTxThread);
	}
	if (pPublicSpi->hSpiThread != NULL)
	{
		CloseHandle(pPublicSpi->hSpiThread);
	}
	if (pPublicSpi->hTxDmaDoneThread != NULL)
	{
		CloseHandle(pPublicSpi->hTxDmaDoneThread);
	}
	if (pPublicSpi->hRxDmaDoneThread != NULL)
	{
		CloseHandle(pPublicSpi->hRxDmaDoneThread);
	}

	// VirtualFree
	if (pPublicSpi->pGPIOregs)
	{
		MmUnmapIoSpace((PVOID)pPublicSpi->pGPIOregs, sizeof(GPIO_REG));
		pPublicSpi->pGPIOregs = NULL;
	}

	if (pPublicSpi->pSPIregs)
	{
		MmUnmapIoSpace((PVOID)pPublicSpi->pSPIregs, sizeof(SPI_REG));
		pPublicSpi->pSPIregs = NULL;
	}

	if (pPublicSpi->pDMAC0regs)
	{
		MmUnmapIoSpace((PVOID)pPublicSpi->pDMAC0regs, sizeof(DMAC_REG));
		pPublicSpi->pDMAC0regs = NULL;
	}

	if (pPublicSpi->pDMAC1regs)
	{
		MmUnmapIoSpace((PVOID)pPublicSpi->pDMAC1regs, sizeof(DMAC_REG));
		pPublicSpi->pDMAC1regs = NULL;
	}

	if (pPublicSpi->pSYSCONregs)
	{
		MmUnmapIoSpace((PVOID)pPublicSpi->pSYSCONregs, sizeof(CMU_CLK_REG));
		pPublicSpi->pSYSCONregs = NULL;
	}

	// Local Free
	LocalFree(pPublicSpi);

CleanUp:
	DBGMSG(SPI_INIT,(TEXT("--[SPI] SPI_Deinit\r\n")));
	return;
}

void SPI_PowerUp (PSPI_PUBLIC_CONTEXT pPublicSpi)
{
	DBGMSG(SPI_PM,(TEXT("[SPI] ++PowerUp()\n\r")));

	if((PSPI_PUBLIC_CONTEXT)pPublicSpi == NULL)
	{
		goto CleanUp;
	}

	DBGMSG(SPI_MSG,(TEXT("[SPI] --PowerUp()\n\r")));

CleanUp:
	return;
}

BOOL SPI_Close (DWORD dwOpen)
{
	PSPI_PUBLIC_CONTEXT         pSpiPublic  = NULL;
	BOOL bResult = TRUE;

	DBGMSG(SPI_INIT,(TEXT("++[SPI] SPI_Close\r\n")));
	if(dwOpen != 0)
	{
		pSpiPublic  = (PSPI_PUBLIC_CONTEXT) dwOpen;
	}
	else
	{
		bResult = FALSE;
		goto CleanUp;
	}

	// Free all data allocated in open
	LocalFree( pSpiPublic );

CleanUp:
	DBGMSG(SPI_INIT,(TEXT("--[SPI] SPI_Close\r\n")));
	return bResult;
}

DWORD SPI_Seek(DWORD Handle, long lDistance, DWORD dwMoveMethod)
{
	return 0;
}

UINT   GetFifoSize(DWORD chnum)
{
	UINT dwFifoSize = 0;

	DBGMSG(SPI_INIT,(TEXT(" ++[SPI] GetFifoSize chnum = %d\r\n"),chnum));                   
	if (chnum == SPI0)
	{
		dwFifoSize = 64;
	}
	else if (chnum == SPI1)
	{
		dwFifoSize = 16;
	}
	else if (chnum == SPI2)
	{
		dwFifoSize = 16;
	}
	else
	{
		ERRMSG((TEXT(" GetFifoSize Invalid SPI Channel \r\n"))); 
		dwFifoSize = 0;
	}
	DBGMSG(SPI_INIT,(TEXT(" --[SPI] GetFifoSize\r\n")));                    
	return dwFifoSize;
}

DWORD   GetTxTriggerLevel(DWORD chnum)
{
	DWORD dwTxTriggerLevel = 0;

	DBGMSG(SPI_INIT,(TEXT(" ++[SPI] GetTxTriggerLevel chnum = %d\r\n"),chnum));                 
	if (chnum == SPI0)
	{
		dwTxTriggerLevel = 10;
	}
	else if (chnum == SPI1)
	{
		dwTxTriggerLevel = 15;
	}
	else if (chnum == SPI2)
	{
		dwTxTriggerLevel = 15;
	}
	else
	{
		ERRMSG((TEXT(" GetTxTriggerLevel Invalid SPI Channel \r\n"))); 
		dwTxTriggerLevel = 0;
	}
	DBGMSG(SPI_INIT,(TEXT(" --[SPI] GetTxTriggerLevel\r\n")));                  
	return dwTxTriggerLevel;
}

#ifdef SPI_FULL_DUPLEX
BOOL SPI_FullDuplex(PSPI_PRIVATE_CONTEXT pSpiIOPrivate, PBYTE pRxBuf, PBYTE pTxBuf, DWORD dwCount)
{
    PSPI_PRIVATE_CONTEXT pSpiPrivate = (PSPI_PRIVATE_CONTEXT)pSpiIOPrivate;
    PSPI_PUBLIC_CONTEXT pSpiPublic = pSpiPrivate->pSpiPublic;
    volatile SPI_REG *pSPIregs = pSpiPublic->pSPIregs;
    ULONG WaitReturn;

    // Check status 
    if(pSpiPrivate->State != STATE_IDLE)
    {
        DBGMSG(1,(TEXT(" [SPI] E :  STATE IS NOT IDLE (%d)\r\n"), pSpiPrivate->State));       
        return FALSE;
    }   
    if((NULL == pTxBuf)||(NULL == pRxBuf))
    {
        DBGMSG(1,(TEXT(" [SPI] E :  In OR Out Buf is NULL \r\n")));       
        return FALSE;
    }          
        
    pSpiPrivate->State = STATE_FULLDUPLEX;	
    
    // Copy TX buf to DMA buf
    memcpy(pSpiPublic->SPIDMAInfo.pVirtDmaSrcBufferAddr,(PBYTE)pTxBuf, dwCount);

    // Mapping Rx/Tx buf to DMA buf 
    pSpiPrivate->pTxBuffer 		= pSpiPublic->SPIDMAInfo.pVirtDmaSrcBufferAddr;
    pSpiPrivate->pTxDMABuffer 	= (LPVOID)pSpiPublic->SPIDMAInfo.DmaSrcAddress;	
    pSpiPrivate->pRxBuffer 		= pSpiPublic->SPIDMAInfo.pVirtDmaDstBufferAddr;
    pSpiPrivate->pRxDMABuffer 	= (LPVOID)pSpiPublic->SPIDMAInfo.DmaDstAddress;    

    //SPI RESET
    pSPIregs->CH_CFG |= SW_RST;
    while(!(pSPIregs->CH_CFG & SW_RST));
    DBGMSG(SPI_USR2,(TEXT("[SPI] HS SPI reset\n")));
    pSPIregs->CH_CFG &= ~SW_RST;

    if(dwCount>0)
    {
        // SPI register configuration
        pSPIregs->CH_CFG         = pSpiPrivate->RxSPIregs.CH_CFG;                 
        pSPIregs->CLK_CFG        = pSpiPrivate->RxSPIregs.CLK_CFG;
        pSPIregs->MODE_CFG       = pSpiPrivate->RxSPIregs.MODE_CFG;     
                
        pSPIregs->MODE_CFG |= TX_DMA_ON|DMA_SINGLE|RX_DMA_ON;
        pSPIregs->CH_CFG |= TX_CH_ON|RX_CH_ON;
        
        // RX DMA start
        DMA_set_channel_source(&pSpiPublic->SPIDMAInfo.g_InputDMA, pSpiPrivate->spi_rx_data_addr, BYTE_UNIT, BURST_1, FIXED);
        DMA_set_channel_destination(&pSpiPublic->SPIDMAInfo.g_InputDMA, (UINT)pSpiPrivate->pRxDMABuffer, BYTE_UNIT, BURST_1, INCREASE);
        DMA_set_channel_transfer_size(&pSpiPublic->SPIDMAInfo.g_InputDMA, dwCount);
        DMA_initialize_LLI(&pSpiPublic->SPIDMAInfo.g_InputDMA, 1);
        DMA_set_LLI_entry(&pSpiPublic->SPIDMAInfo.g_InputDMA, 0, LLI_FIRST_ENTRY, pSpiPrivate->spi_rx_data_addr,(UINT)pSpiPrivate->pRxDMABuffer, dwCount);
        DMA_channel_start(&pSpiPublic->SPIDMAInfo.g_InputDMA);

        // TX DMA start
        DMA_set_channel_source(&pSpiPublic->SPIDMAInfo.g_OutputDMA, (UINT)pSpiPrivate->pTxDMABuffer, BYTE_UNIT, BURST_1, INCREASE);
        DMA_set_channel_destination(&pSpiPublic->SPIDMAInfo.g_OutputDMA, pSpiPrivate->spi_tx_data_addr, BYTE_UNIT, BURST_1, FIXED);
        DMA_set_channel_transfer_size(&pSpiPublic->SPIDMAInfo.g_OutputDMA, dwCount);
        DMA_initialize_LLI(&pSpiPublic->SPIDMAInfo.g_OutputDMA, 1);
        DMA_set_LLI_entry(&pSpiPublic->SPIDMAInfo.g_OutputDMA, 0, LLI_FIRST_ENTRY, (UINT)pSpiPrivate->pTxDMABuffer,pSpiPrivate->spi_tx_data_addr, dwCount);        
        DMA_channel_start(&pSpiPublic->SPIDMAInfo.g_OutputDMA);

        if(pSpiPrivate->dwMode == SPI_MASTER_MODE)
        {
        	DBGMSG(SPI_USR2,(TEXT("[SPI] Thread for FD : MASTER MODE \r\n")));
        	MASTER_CS_ENABLE;
        } 

        //Wait DMA done Interrupt
        // Waiting for RX DMA done interrupt is enough
        WaitReturn=WaitForSingleObject(pSpiPublic->hRxDmaDoneEvent, READ_TIME_OUT_CONSTANT);
        if(WAIT_TIMEOUT == WaitReturn)
        {
            DBGMSG(1,(TEXT("[SPI] E : RX DMA TIMEOUT\r\n")));
        }

        DMA_clear_interrupt_pending(&pSpiPublic->SPIDMAInfo.g_InputDMA);
        DMA_set_interrupt_mask(&pSpiPublic->SPIDMAInfo.g_InputDMA);
        InterruptDone(pSpiPublic->dwRxDmaDoneSysIntr);
        DMA_clear_interrupt_mask(&pSpiPublic->SPIDMAInfo.g_InputDMA); 
        
        while(!(pSPIregs ->SPI_STATUS & TX_DONE));
        if(pSpiPrivate->dwMode == SPI_MASTER_MODE)
        {
        	DBGMSG(SPI_USR2,(TEXT("[SPI] Thread for FD : MASTER MODE \r\n")));
        	MASTER_CS_DISABLE;
        }

        // Copy RX DMA Buf to RX Buf
        memcpy(pRxBuf, pSpiPublic->SPIDMAInfo.pVirtDmaDstBufferAddr, dwCount);

    }
    else
    {
        return FALSE;
    }

    pSpiPrivate->State = STATE_IDLE;	
    return TRUE;
    
}
#endif 
