/*++
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.
Copyright (c) 2001. Samsung Electronics, co. ltd  All rights reserved.
--*/

#include "windows.h"
#include "nkintr.h"
#include "oalintr.h"
#include "shx.h"
#include "p2.h"
#include "p2debug.h"
#include "tchaud.h"
#include "drv_glob.h"
#include <memory.h>
#ifdef MODULE_CERTIFY
#include "key1024.c"    // OEMLoadInit/OEMLoadModule implementation and the public key used for module signature verification.
#endif
//#include "timer.h"
#include <S2440.h>
#include <pehdr.h>
#include <romldr.h>


extern const unsigned short ScreenBitmap[];

/*
    @doc    EXTERNAL KERNEL HAL

    @module cfwp2.c - P2 HW Support | 
        OEM support Functions for the Windows CE P2 Platform.
    
    @xref <f OEMInit> <f OEMInterruptEnable> <f OEMInterruptDisable> 
          <f OEMInterruptDone> <l HAL Overview.Windows CE Kernel OEM Interface>
          
    @topic Windows CE Kernel OEM Interface |
          This defines the HAL layer - OEM and platform dependent pieces of
          code which we expect the OEM to deliver to us. There are three pieces
          of OEM deliverable code  - the bootstrap loader & monitor (for 
          debugging), the HAL portions which are interfaces between the kernel
          and the firmware, and the driver interface. This topic covers just 
          the HAL portion.

          The philosophy is to keep the HAL layer as simple as possible.  The 
          HAL should not be confused with the machine or CPU independence. HAL 
          is specific for a particular CPU and platform. It includes interfaces 
          for the following devices:<nl>
          Real Time Clock<nl>
          Interval Timer (used for the scheduler operation) <nl>
          Interrupt handlers and support <nl>

          Note that it does not include abstractions for devices like the DMA 
          controller etc. since the kernel does not use one. Also note that the
          list could change for different CPU's and platforms - for instance, 
          some chips might include a lot of peripheral devices (like the 
          interval timer) on the CPU chip itself, removing the need for a 
          separate interface for them.

          The interfaces for the real time clock and interval timer are still 
          being developed. But they should in general be extremely simple and
          straightforward. For details on the interrupt support model in the 
          kernel look at <l Interrupt Support Overview.Kernel Interrupt Support>

    @xref <l Interrupt Support Overview.Kernel Interrupt Support>
          <f OEMInit> <f OEMInterruptEnable> <f OEMInterruptDisable> 
          <f OEMInterruptDone> <f HookInterrupt>
             
 */


unsigned long OEMClockFreq;				// OEM clock frequency is used only in OAL

//
// Kernel global variables used by GetIdleTime( ) to determine CPU utilization
//
extern DWORD idleconv; 					// translation constant in 1 ms units
extern DWORD curridlehigh, curridlelow;	// 64-bit idle time in ms

extern int P2ISR();
extern void InitClock();
extern void HalCpuInit();
extern void HalTimerInit();
extern void HalSleep(DWORD);

extern void TIMERINIT();
extern void InitDebugEther(void);

static void InitDisplay(void);
static void OEMInitInterrupts(void);
static void InitSDMMC(void);

typedef volatile WORD *PVWORD;    /* pointer to a volatile word */
typedef volatile DWORD *PVDWORD;    /* pointer to a volatile dword */


#define REG(base, id)   (*(PVWORD)((base)+(id)))
#define REG32(base, id)   (*(PVDWORD)((base)+(id)))

#define SDIO_FOR_100BD	0
/*
#if (CE_MAJOR_VER == 0x0003)
	//Cedar
	// The kernel exports...
	#ifdef AddrCurMSec
		// Some kernels export a pointer to the CurMSec variable.
		static volatile DWORD * pCurMSec  = (volatile DWORD *) AddrCurMSec;
		static volatile DWORD * pDiffMSec = (volatile DWORD *) AddrDiffMSec;
	#else
		extern volatile DWORD CurMSec;
		extern volatile DWORD DiffMSec;
		static volatile DWORD * pCurMSec = &CurMSec;
		static volatile DWORD * pDiffMSec = &DiffMSec;
	#endif

	extern DWORD dwSleepMin;
	extern DWORD dwPartialDiffMSec;
	extern DWORD ticksleft;

#else
// dougfir or later
//
	#ifdef AddrCurMSec
		// Some kernels export a pointer to the CurMSec variable.
		static volatile DWORD * pCurMSec  = (volatile DWORD *) AddrCurMSec;
	#else
		extern volatile DWORD CurMSec;
		static volatile DWORD * pCurMSec = &CurMSec;
	#endif
	
extern DWORD dwReschedTime;
#endif
*/

// dougfir or later
//
	#ifdef AddrCurMSec
		// Some kernels export a pointer to the CurMSec variable.
		static volatile DWORD * pCurMSec  = (volatile DWORD *) AddrCurMSec;
	#else
		extern volatile DWORD CurMSec;
		static volatile DWORD * pCurMSec = &CurMSec;
	#endif
	
extern DWORD dwReschedTime;


extern BOOL fIntrTime;
extern BOOL bProfileTimerRunning;

volatile ULARGE_INTEGER CurTicks = { 0, 0 };
volatile ULARGE_INTEGER * pCurTicks = &CurTicks;

extern DWORD dwReschedIncrement;
extern DWORD OEMCount1ms;
//extern void Camera_Initialize(void);


#define NOT_FIXEDUP		   (DWORD*)-1
DWORD *pdwXIPLoc = NOT_FIXEDUP;
extern  ROMChain_t         *OEMRomChain;

/*
    @func   void | InitRomChain | Collects chain information for all image regions for the kernel.
    @rdesc  N/A.
    @comm    
    @xref   
*/
/*
void InitRomChain(void)
{
	static		ROMChain_t	s_pNextRom[MAX_ROM] = {0};
	DWORD		dwRomCount = 0;
    DWORD       dwChainCount = 0;
    DWORD		*pdwCurXIP;
    DWORD       dwNumXIPs;
    PXIPCHAIN_ENTRY pChainEntry = NULL;

    if(pdwXIPLoc == NOT_FIXEDUP)
	{
        return;  // no chain or not fixed up properly
    }

    // set the top bit to mark it as a virtual address
    pdwCurXIP = (DWORD*)(((DWORD)pdwXIPLoc) | 0x80000000);

    // first DWORD is number of XIPs
    dwNumXIPs = (*pdwCurXIP);

    if(dwNumXIPs > MAX_ROM)
	{
      lpWriteDebugStringFunc(TEXT("ERROR: Number of XIPs exceeds MAX\n"));
      return;
    }

    pChainEntry = (PXIPCHAIN_ENTRY)(pdwCurXIP + 1);

    while(dwChainCount < dwNumXIPs)
    {
        if ((pChainEntry->usFlags & ROMXIP_OK_TO_LOAD) &&  // flags indicates valid XIP
            *(LPDWORD)(((DWORD)(pChainEntry->pvAddr)) + ROM_SIGNATURE_OFFSET) == ROM_SIGNATURE)
        {
            s_pNextRom[dwRomCount].pTOC = *(ROMHDR **)(((DWORD)(pChainEntry->pvAddr)) + ROM_SIGNATURE_OFFSET + 4);
            s_pNextRom[dwRomCount].pNext = NULL;

            if (dwRomCount != 0)
            {
                s_pNextRom[dwRomCount-1].pNext = &s_pNextRom[dwRomCount];
            }
            else
            {
                OEMRomChain = s_pNextRom;
            }
            dwRomCount++;
        }
        else
        {
            lpWriteDebugStringFunc( _T("Invalid XIP found\n") );
        }

        ++pChainEntry;
		dwChainCount++;
	}
}
*/

#define FROM_BCD(n)		((((n) >> 4) * 10) + ((n) & 0xf))


//------------------------------------------------------------------------------
//
//  @func   void | OEMInit | Initialize Hardware Interfaces
//  @rdesc  none
//  @comm   OEMInit is called by the kernel after it has performed minimal
//          initialization. Interrupts are disabled and the kernel is not
//          ready to handle exceptions. The only kernel service available
//          to this function is <f HookInterrupt>. This should be used to 
//          install ISR's for all the hardware interrupts to be handled by
//          the firmware. Note that ISR's must be installed for any interrupt
//          that is to be routed to a device driver - otherwise the 
//          <f InterruptInitialize> call from the driver will fail.
//  @xref   <l Overview.Windows CE Kernel OEM Interface> <f HookInterrupt>
//          <f InterruptInitialize>
//            
//------------------------------------------------------------------------------
void OEMInit()  
{
	volatile IOPreg *s2440IOP = (IOPreg *)IOP_BASE;

	// Instead of calling OEMWriteDebugString directly, call through exported
	// function pointer.  This will allow these messages to be seen if debug
	// message output is redirected to Ethernet or the parallel port.  Otherwise,
	// lpWriteDebugStringFunc == OEMWriteDebugString.
	lpWriteDebugStringFunc(TEXT("\nWindows CE Firmware Init\r\n"));


#ifdef MODULE_CERTIFY
	//
	// Set the module signature verification hooks
	//
	pOEMLoadInit   = OEMLoadInit;
	pOEMLoadModule = OEMLoadModule;
	//
	// Init the signature verification public key
	//
	InitPubKey(g_bSignPublicKeyBlob,sizeof(g_bSignPublicKeyBlob));
#endif

    //
    // Set up translation constant for GetIdleTime() (1 ms units).
    // Note: Since curridlehigh, curridlelow is counting in ms, and GetIdleTime()
    // reports in ms, the conversion ratio is one.  If curridlehigh, curridlelow
    // were using other units (like ticks), then the conversion would be calculated
    // from the clock frequency.
    //
    idleconv = 1;

	// Initialize interrupts.
	//
    lpWriteDebugStringFunc(TEXT("INFO: Initializing system interrupts...\r\n"));
	OEMInitInterrupts();

	// Initialize the system clock(s).
	//
    lpWriteDebugStringFunc(TEXT("INFO: Initializing system clock(s)...\r\n"));
    InitClock();

    // Initialize driver globals area.
	//
    lpWriteDebugStringFunc(TEXT("INFO: Initializing driver globals area...\r\n"));
    memset((PVOID)DRIVER_GLOBALS_ZEROINIT_START, 0, DRIVER_GLOBALS_ZEROINIT_SIZE);
    
    // Initialize S2440X01 LCD controller
	InitDisplay();
	
    // Initialize debug Ethernet (KITL) connection.
    //
	// InitDebugEther();	
 
	// Initialize GPIO	/// ;;; SHL
	s2440IOP->rPAD9 = (1<<12) | (0<<11);
	//s2440IOP->rGPJCON = 0x016aaaa;
	//s2440IOP->rGPJUP  = ~((0<<12) | (1<<11));

	s2440IOP->rGPHCON = (s2440IOP->rGPHCON & ~(0xf<<18)) | (1<<20) | (1<<18);	// CLKOUT1, CLKOUT0

	s2440IOP->rDSC0 = 0x3ff;
	s2440IOP->rDSC1 = 0x3fffffff;

	// camera
	//Camera_Initialize();
	//s2440IOP->rGPJCON = 0x2aaaaaa;
	//s2440IOP->rGPJUP  = 0x1fff;

	InitSDMMC();

	// Initialize the ROM chain (multi-region).
	//
//	InitRomChain();
	lpWriteDebugStringFunc(TEXT("OEMInit Done...\r\n"));
}


//------------------------------------------------------------------------------
//  
//  @func   BOOL | OEMInterruptEnable | Enable a hardware interrupt
//  @rdesc  Returns TRUE if valid interrupt ID or FALSE if invalid ID.
//  @comm   This function is called by the Kernel when a device driver
//          calls <f InterruptInitialize>. The system is not preemptible when this
//          function is called.
//  @xref   <l Overview.Windows CE Kernel OEM Interface> <f InterruptInitialize>
//  
//------------------------------------------------------------------------------
BOOL 
OEMInterruptEnable(DWORD  idInt,	// @parm Interrupt ID to be enabled. See <l Interrupt ID's.Interrupt ID's>  for a list of possble values.
				   LPVOID pvData,	// @parm ptr to data passed in in the <f InterruptInitialize> call
		           DWORD  cbData)	// @parm Size of data pointed to be <p pvData>
{
	volatile INTreg *s2440INT = (INTreg *)INT_BASE;
	volatile IOPreg *s2440IOP = (IOPreg *)IOP_BASE;
	volatile MMCreg *s2440SDIO = (MMCreg *)MMC_BACE;
	BOOL bRet = TRUE;

	INTERRUPTS_OFF();
	
	switch (idInt) 
	{
	case SYSINTR_VMINI:		// Vmini.
		//return (TRUE);
		break;

	case SYSINTR_BREAK:		// There is no halt button on P2.
		//return(FALSE);
        break;


    case SYSINTR_DMA0:
        s2440INT->rINTMSK &= ~BIT_DMA0; // SDIO DMA interrupt
		//RETAILMSG(1,(TEXT("::: SYSINTR_DMA0    OEMInterruptDisable\r\n")));
       	break;

	case SYSINTR_SDMMC:
		s2440INT->rINTMSK &= ~BIT_MMC;
		//RETAILMSG(1,(TEXT("::: SYSINTR_SDMMC    OEMInterruptDisable\r\n")));
		break;        

	case SYSINTR_SDMMC_SDIO_INTERRUPT:
		s2440INT->rINTMSK &= ~BIT_MMC;
		//RETAILMSG(1,(TEXT("::: SYSINTR_SDMMC_SDIO_INTERRUPT    OEMInterruptEnable\r\n")));		
		break;

	case SYSINTR_SDMMC_CARD_DETECT:
#if SDIO_FOR_100BD		// for b'd revision 1.00
		s2440IOP->rEINTPEND  = (1 << 18);
		s2440IOP->rEINTMASK &= ~(1 << 18);
		//s2440INT->rSRCPND	= BIT_EINT8_23;
		// if (s2440INT->rINTPND & BIT_EINT8_23) 
	
		// s2440INT->rINTMSK  &= ~BIT_EINT8_23;
		//RETAILMSG(1,(TEXT("::: SYSINTR_SDMMC_CARD_DETECT    OEMInterruptEnable\r\n")));
#else					// for b'd revision 0.17
		s2440IOP->rEINTPEND  = (1 << 16);
		s2440IOP->rEINTMASK &= ~(1 << 16);  
#endif
		s2440INT->rSRCPND = BIT_EINT8_23;
		s2440INT->rINTPND = BIT_EINT8_23;
		s2440INT->rINTMSK &= ~BIT_EINT8_23;

		break;     

    case SYSINTR_TOUCH:
		//RETAILMSG(0,(TEXT("OEMInterruptEnable:TOUCH\n\r\n")));
        break;
	
    case SYSINTR_TOUCH_CHANGED:
		//RETAILMSG(0,(TEXT("OEMInterruptEnable:TOUCH CHANGED\r\n\r\n")));
		s2440INT->rINTMSK &= ~BIT_ADC;
		s2440INT->rINTSUBMSK &= ~INTSUB_TC;
        break;

	case SYSINTR_KEYBOARD:	// Keyboard on EINT1.
/*
		s2440INT->rSRCPND  = BIT_EINT1;
		// S3C2440X Developer Notice (page 4) warns against writing a 1 to a 0 bit in the INTPND register.
		if (s2440INT->rINTPND & BIT_EINT1) s2440INT->rINTPND = BIT_EINT1;
*/
		s2440INT->rINTMSK &= ~BIT_EINT1;
		break;

	case SYSINTR_SERIAL:	// Serial port.
		s2440INT->rSUBSRCPND  = (INTSUB_RXD0 | INTSUB_TXD0 | INTSUB_ERR0);
		s2440INT->rINTSUBMSK &= ~INTSUB_RXD0;
		s2440INT->rINTSUBMSK &= ~INTSUB_TXD0;
		s2440INT->rINTSUBMSK &= ~INTSUB_ERR0;
		s2440INT->rSRCPND     = BIT_UART0;
		// S3C2440X Developer Notice (page 4) warns against writing a 1 to a 0 bit in the INTPND register.
		if (s2440INT->rINTPND & BIT_UART0) s2440INT->rINTPND = BIT_UART0;
		s2440INT->rINTMSK    &= ~BIT_UART0;
		break;

	case SYSINTR_SERIAL2:  
    		s2440INT->rSUBSRCPND  = (INTSUB_RXD1 | INTSUB_TXD1 | INTSUB_ERR1); 
    		s2440INT->rINTSUBMSK &= ~INTSUB_RXD1; 
    		s2440INT->rINTSUBMSK &= ~INTSUB_TXD1; 
    		s2440INT->rINTSUBMSK &= ~INTSUB_ERR1; 
    		s2440INT->rSRCPND     = BIT_UART1; 
    		// S3C2440X Developer Notice (page 4) warns against writing a 1 to a 0 bit in the INTPND register. 
    		if (s2440INT->rINTPND & BIT_UART1) s2440INT->rINTPND = BIT_UART1; 
    		s2440INT->rINTMSK    &= ~BIT_UART1; 
    		break; 

	case SYSINTR_SERIAL3:  
		s2440INT->rSUBSRCPND  = (INTSUB_RXD2 | INTSUB_TXD2 | INTSUB_ERR2);
		s2440INT->rINTSUBMSK &= ~INTSUB_RXD2;
		s2440INT->rINTSUBMSK &= ~INTSUB_TXD2;
		s2440INT->rINTSUBMSK &= ~INTSUB_ERR2;
		s2440INT->rSRCPND     = BIT_UART2;
		// S3C2440X Developer Notice (page 4) warns against writing a 1 to a 0 bit in the INTPND register.
		if (s2440INT->rINTPND & BIT_UART2) s2440INT->rINTPND = BIT_UART2;
		s2440INT->rINTMSK    &= ~BIT_UART2;
		break;

// update audio old ;;; SHL
//	case SYSINTR_AUDIO:		// Audio controller (the controller uses both DMA1 and DMA2 interrupts).
//		// DMA1 (input).
//		//
//		s2440INT->rSRCPND  = BIT_DMA1;
//		// S3C2440X Developer Notice (page 4) warns against writing a 1 to a 0 bit in the INTPND register.
//		if (s2440INT->rINTPND & BIT_DMA1) s2440INT->rINTPND = BIT_DMA1;
//		s2440INT->rINTMSK &= ~BIT_DMA1;
//		// DMA2 (output).
//		//
//		s2440INT->rSRCPND  = BIT_DMA2;
//		// S3C2440X Developer Notice (page 4) warns against writing a 1 to a 0 bit in the INTPND register.
//		if (s2440INT->rINTPND & BIT_DMA2) s2440INT->rINTPND = BIT_DMA2;
//		s2440INT->rINTMSK &= ~BIT_DMA2;
//		break;

// update audio bug.
	case SYSINTR_AUDIO:		// Audio controller (the controller uses both DMA1 and DMA2 interrupts).
		// DMA1 (input).
		//
		s2440INT->rINTMSK &= ~BIT_DMA1;
		// DMA2 (output).
		s2440INT->rINTMSK &= ~BIT_DMA2;
		break;

	case SYSINTR_ADC:
		//return(FALSE);
		break;

	case SYSINTR_PCMCIA_LEVEL:	// PCMCIA data on EINT8.
		s2440INT->rINTMSK  &= ~BIT_EINT8_23;
		//s2440INT->rSRCPND  = BIT_EINT8_23;
		//s2440INT->rINTPND  = BIT_EINT8_23;
		s2440IOP->rEINTMASK &= ~0x100;
		//s2440IOP->rEINTPEND  = 0x100;
		//RETAILMSG(1,(TEXT("::: SYSINTR_PCMCIA_LEVEL    OEMInterruptEnable\r\n")));
		break;

	case SYSINTR_PCMCIA_EDGE:
		//return(FALSE);
		break;

	case SYSINTR_PCMCIA_STATE:	// PCMCIA insertion interrupt.
		s2440INT->rSRCPND  = BIT_EINT3;  // to clear the previous pending states
		// S3C2440X Developer Notice (page 4) warns against writing a 1 to a 0 bit in the INTPND register.
		if (s2440INT->rINTPND & BIT_EINT3) s2440INT->rINTPND = BIT_EINT3;
		s2440INT->rINTMSK &= ~BIT_EINT3;
		//RETAILMSG(1,(TEXT("::: SYSINTR_PCMCIA_STATE    OEMInterruptEnable\r\n")));
		break;

	case SYSINTR_TIMING:
		//return(FALSE);
		break;

    case SYSINTR_ETHER:			// Ethernet on EINT9.
		s2440IOP->rEINTPEND   = 0x200;
		s2440IOP->rEINTMASK  &= ~0x200;
		//s2440INT->rSRCPND     = BIT_EINT8_23;	// by shim
		// S3C2440X Developer Notice (page 4) warns against writing a 1 to a 0 bit in the INTPND register.
		if (s2440INT->rINTPND & BIT_EINT8_23) s2440INT->rINTPND = BIT_EINT8_23;
		s2440INT->rINTMSK    &= ~BIT_EINT8_23;
		//RETAILMSG(1,(TEXT("::: SYSINTR_ETHER    OEMInterruptEnable\r\n")));
        break;

#if 0
	case SYSINTR_USB:
   		// USB host interrupt enable bit. by hjcho
   		s2440INT->rINTMSK &= ~BIT_USBH;
   		break;

	case SYSINTR_USBD:
   		s2440INT->rINTMSK &= ~BIT_USBD;
		//RETAILMSG(1,(TEXT("::: SYSINTR_USBD     OEMInterruptEnable\r\n")));
   		break;
#else
	case SYSINTR_USB:			// USB host.
		s2440INT->rSRCPND  = BIT_USBH;
		// S3C2440X Developer Notice (page 4) warns against writing a 1 to a 0 bit in the INTPND register.
		if (s2440INT->rINTPND & BIT_USBH) s2440INT->rINTPND = BIT_USBH;
		s2440INT->rINTMSK &= ~BIT_USBH;
		break;

	case SYSINTR_USBD:
		s2440INT->rSRCPND  = BIT_USBD;
		// S3C2440X Developer Notice (page 4) warns against writing a 1 to a 0 bit in the INTPND register.
		if (s2440INT->rINTPND & BIT_USBD) s2440INT->rINTPND = BIT_USBD;
		s2440INT->rINTMSK &= ~BIT_USBD;
		RETAILMSG(1, (TEXT("USB enable interrutp\r\n")));
		break;
#endif

	case SYSINTR_POWER:
		s2440INT->rSRCPND  = BIT_EINT0;
		// S3C2440X Developer Notice (page 4) warns against writing a 1 to a 0 bit in the INTPND register.
		if (s2440INT->rINTPND & BIT_EINT0) s2440INT->rINTPND = BIT_EINT0;
		s2440INT->rINTMSK &= ~BIT_EINT0;
		s2440INT->rSRCPND  = BIT_EINT2;
		// S3C2440X Developer Notice (page 4) warns against writing a 1 to a 0 bit in the INTPND register.
		if (s2440INT->rINTPND & BIT_EINT2) s2440INT->rINTPND = BIT_EINT2;
		s2440INT->rINTMSK &= ~BIT_EINT2;
		break;     

	case SYSINTR_CAM:
		s2440INT->rINTSUBMSK &= ~(BIT_SUB_CAM_P | BIT_SUB_CAM_C);
		s2440INT->rINTMSK &= ~BIT_CAM;
		break;

    case SYSINTR_IIC:
        s2440INT->rINTMSK &= ~BIT_IIC;
       break;

	default:
		bRet = FALSE;	/* unsupported interrupt value */
		//return(FALSE);
        break;
	}
    
	INTERRUPTS_ON();
    	
    return bRet;
    
	//return(TRUE);
}



//------------------------------------------------------------------------------
//  
//  @func   BOOL | OEMInterruptDisable | Disable a hardware interrupt
//  @rdesc  none
//  @comm   OEMInterruptDisable is called by the Kernel when a device driver
//          calls <f InterruptDisable>. The system is not preemtible when this
//          function is called.
//  @xref   <l Overview.Windows CE Kernel OEM Interface> <f InterruptDisable>
//  
//------------------------------------------------------------------------------
void 
OEMInterruptDisable(DWORD idInt)	// @parm Interrupt ID to be disabled. See <t Interrupt ID's>
									// for the list of possible values.
{
	volatile INTreg *s2440INT = (INTreg *)INT_BASE;
	volatile IOPreg *s2440IOP = (IOPreg *)IOP_BASE;
	volatile MMCreg *s2440SDIO = (MMCreg *)MMC_BACE;

    INTERRUPTS_OFF();

	switch (idInt) 
	{
	case SYSINTR_BREAK:		// There is no halt button on P2.
		break;

    case SYSINTR_DMA0:
        s2440INT->rINTMSK |= BIT_DMA0; // SDIO DMA interrupt
		//RETAILMSG(1,(TEXT("::: SYSINTR_DMA0    OEMInterruptDisable\r\n")));
		break;

	case SYSINTR_SDMMC:
		s2440INT->rINTMSK |= BIT_MMC;
		//RETAILMSG(1,(TEXT("::: SYSINTR_SDMMC    OEMInterruptDisable\r\n")));
		break;

	case SYSINTR_SDMMC_SDIO_INTERRUPT:
		s2440INT->rINTMSK |= BIT_MMC;
		s2440SDIO->rSDIINTMSK &= ~(0x1<<12);		// interrupt from SDIO card
		//RETAILMSG(1,(TEXT("::: SYSINTR_SDMMC_SDIO_INTERRUPT    OEMInterruptDisable\r\n")));
		break;

	case SYSINTR_SDMMC_CARD_DETECT:
#if SDIO_FOR_100BD		// for b'd revision 1.00
		s2440IOP->rEINTMASK |= (1 << 18);
#else					// for b'd revision 0.17
		s2440IOP->rEINTMASK |= (1 << 16);
#endif
		s2440INT->rINTMSK    |= BIT_EINT8_23;
		//RETAILMSG(1,(TEXT("::: SYSINTR_SDMMC_CARD_DETECT    OEMInterruptDisable\r\n")));
		break;        


	case SYSINTR_TOUCH:
	    break;
	
	case SYSINTR_TOUCH_CHANGED:
		s2440INT->rINTMSK |= BIT_ADC;
		s2440INT->rINTSUBMSK |= INTSUB_TC;
	    break;

	case SYSINTR_KEYBOARD:
		s2440INT->rINTMSK |= BIT_EINT1;
		break;

	case SYSINTR_SERIAL:
		s2440INT->rINTMSK    |= BIT_UART0;
		s2440INT->rINTSUBMSK |= INTSUB_RXD0;
		s2440INT->rINTSUBMSK |= INTSUB_TXD0;
		s2440INT->rINTSUBMSK |= INTSUB_ERR0;
		break;

	case SYSINTR_SERIAL2: 	
	  s2440INT->rINTMSK    |= BIT_UART1; 
	  s2440INT->rINTSUBMSK |= INTSUB_RXD1; 
	  s2440INT->rINTSUBMSK |= INTSUB_TXD1; 
	  s2440INT->rINTSUBMSK |= INTSUB_ERR1; 
	  break; 
	  
	case SYSINTR_SERIAL3:
		s2440INT->rINTMSK    |= BIT_UART2;
		s2440INT->rINTSUBMSK |= INTSUB_RXD2;
		s2440INT->rINTSUBMSK |= INTSUB_TXD2;
		s2440INT->rINTSUBMSK |= INTSUB_ERR2;
		break;  

	case SYSINTR_AUDIO:
        s2440INT->rINTMSK |= BIT_DMA1;	// Audio input DMA.
		s2440INT->rINTMSK |= BIT_DMA2;	// Audio output DMA.
		break;

	case SYSINTR_ADC:
		break;

    case SYSINTR_PCMCIA_LEVEL:
		s2440IOP->rEINTMASK |= 0x100;
		s2440INT->rINTMSK   |= BIT_EINT8_23;
		//RETAILMSG(1,(TEXT("::: SYSINTR_PCMCIA_LEVEL    OEMInterruptDisable\r\n")));
        break;

	case SYSINTR_PCMCIA_EDGE:
		break;

	case SYSINTR_PCMCIA_STATE:
		s2440INT->rINTMSK |= BIT_EINT3;
		//RETAILMSG(1,(TEXT("::: SYSINTR_PCMCIA_STATE    OEMInterruptDisable\r\n")));
		break;

    case SYSINTR_ETHER:
		s2440INT->rINTMSK   |= BIT_EINT8_23;
		s2440IOP->rEINTMASK |= 0x200;
		//RETAILMSG(1,(TEXT("::: SYSINTR_ETHER    OEMInterruptDisable\r\n")));
        break;        

	case SYSINTR_USB:
		s2440INT->rINTMSK |= BIT_USBH;
		break;
        		
	case SYSINTR_USBD:
		s2440INT->rINTMSK |= BIT_USBD;
		//RETAILMSG(1,(TEXT("::: SYSINTR_USBD    OEMInterruptDisable\r\n")));
		break;
        
	case SYSINTR_POWER:
		s2440INT->rINTMSK |= BIT_EINT0;
		s2440INT->rINTMSK |= BIT_EINT2;
		break;        

	case SYSINTR_CAM:
		s2440INT->rINTMSK |= BIT_CAM;
		s2440INT->rINTSUBMSK |= (INTSUB_CAM_P | INTSUB_CAM_C);
		break;

    case SYSINTR_IIC:
        s2440INT->rINTMSK |= BIT_IIC;
       break;

	default:
		break;
	}
	INTERRUPTS_ON();
}


//------------------------------------------------------------------------------
//  
//  @func   BOOL | OEMInterruptDone | Signal completion of interrupt processing
//  @rdesc  none
//  @comm   OEMInterruptDone is called by the Kernel when a device driver
//          calls <f InterruptDone>. The system is not preemtible when this
//          function is called.
//  @xref   <l Overview.Kernel Interrupt Support> <f InterruptDone>
//  
//------------------------------------------------------------------------------
void 
OEMInterruptDone(DWORD idInt)	// @parm Interrupt ID. See <t Interrupt ID's>
                    			// for the list of possible values.
{
	volatile INTreg *s2440INT	= (INTreg *)INT_BASE;
	volatile IOPreg *s2440IOP	= (IOPreg *)IOP_BASE;    

	INTERRUPTS_OFF();
	
	switch (idInt) 
	{

    case SYSINTR_DMA0:
        s2440INT->rINTMSK &= ~BIT_DMA0; // SDIO DMA interrupt
		//RETAILMSG(1,(TEXT("::: SYSINTR_DMA0    OEMInterruptDone\r\n")));
		break;

	case SYSINTR_SDMMC:
		s2440INT->rINTMSK &= ~BIT_MMC;
		//RETAILMSG(1,(TEXT("::: SYSINTR_SDMMC    OEMInterruptDone\r\n")));
		break;

	case SYSINTR_SDMMC_SDIO_INTERRUPT:
		s2440INT->rINTMSK &= ~BIT_MMC;
		//RETAILMSG(1,(TEXT("::: SYSINTR_SDMMC_SDIO_INTERRUPT    OEMInterruptDone\r\n")));
		break;

	case SYSINTR_SDMMC_CARD_DETECT:
#if SDIO_FOR_100BD		// for b'd revision 1.00
		s2440IOP->rEINTPEND  = (1<<18);
		s2440IOP->rEINTMASK &= ~(1 << 18);
		//RETAILMSG(1,(TEXT("::: SYSINTR_SDMMC_CARD_DETECT    OEMInterruptDone\r\n")));
#else					// for b'd revision 0.17
		s2440IOP->rEINTPEND  = (1<<16);
		s2440IOP->rEINTMASK &= ~(1 << 16);
#endif
		s2440INT->rINTMSK   &= ~BIT_EINT8_23;
		break;        

    case SYSINTR_TOUCH:
        /*
         * Nothing has to be done here as interrupts are masked and unmasked by the touch
         * handler in the HAL.
         */
		s2440INT->rINTMSK &= ~BIT_TIMER1;
        break;

    case SYSINTR_TOUCH_CHANGED:
        /*
         * Nothing has to be done here as interrupts are masked and unmasked by the touch
         * handler in the HAL.
         */
		s2440INT->rINTMSK &= ~BIT_ADC;
		s2440INT->rINTSUBMSK &= ~INTSUB_TC;
		//RETAILMSG(0,(TEXT("OEMInterruptDone:TOUCH CHANGED\n\r\n")));
        break;

	case SYSINTR_KEYBOARD:
		s2440INT->rINTMSK &= ~BIT_EINT1;
		break;

	case SYSINTR_SERIAL:
		s2440INT->rINTMSK    &= ~BIT_UART0;
		s2440INT->rINTSUBMSK &= ~INTSUB_RXD0;
		break;

	case SYSINTR_SERIAL2: 	
    		s2440INT->rINTMSK    &= ~BIT_UART1; 
    		s2440INT->rINTSUBMSK &= ~INTSUB_RXD1; 
    		break; 
    
	case SYSINTR_SERIAL3: 
		s2440INT->rINTMSK    &= ~BIT_UART2;
		s2440INT->rINTSUBMSK &= ~INTSUB_RXD2;
		break;

	case SYSINTR_AUDIO:
		// DMA1 is for audio input.
		// DMA2 is for audio output.
		s2440INT->rSRCPND = (BIT_DMA1 | BIT_DMA2); 
		if (s2440INT->rINTPND & BIT_DMA1) s2440INT->rINTPND = BIT_DMA1;
		if (s2440INT->rINTPND & BIT_DMA2) s2440INT->rINTPND = BIT_DMA2;
        s2440INT->rINTMSK &= ~BIT_DMA1;
        s2440INT->rINTMSK &= ~BIT_DMA2;
		break;

	case SYSINTR_ADC:
		break;

	case SYSINTR_PCMCIA_LEVEL:
		s2440INT->rSRCPND	= BIT_EINT8_23;
		if (s2440INT->rINTPND & BIT_EINT8_23) s2440INT->rINTPND = BIT_EINT8_23; 
		s2440INT->rINTMSK   &= ~BIT_EINT8_23;
		s2440IOP->rEINTMASK &= ~(1<<8);
		//RETAILMSG(1,(TEXT("::: SYSINTR_PCMCIA_LEVEL    OEMInterruptDone\r\n")));
		break;

	case SYSINTR_PCMCIA_EDGE:
		//RETAILMSG(1,(TEXT("::: SYSINTR_PCMCIA_EDGE    OEMInterruptDone\r\n")));
		break;

	case SYSINTR_PCMCIA_STATE:
		s2440INT->rINTMSK &= ~BIT_EINT3;
		//RETAILMSG(1,(TEXT("::: SYSINTR_PCMCIA_STATE    OEMInterruptDone\r\n")));
		break;

	case SYSINTR_ETHER:
		s2440INT->rINTMSK   &= ~BIT_EINT8_23;
		s2440IOP->rEINTMASK &= ~0x200;
		//RETAILMSG(1, (TEXT("::: SYSINTR_USBD	OEMInterruptDone\r\n")));
		break;
			        
	case SYSINTR_USB:
		s2440INT->rINTMSK &= ~BIT_USBH;
		break;

	case SYSINTR_USBD:
		s2440INT->rINTMSK &= ~BIT_USBD;
		//RETAILMSG(1,(TEXT("::: SYSINTR_USBD    OEMInterruptDone\r\n")));
		break;
        
	case SYSINTR_POWER:
		s2440INT->rSRCPND = BIT_EINT0;
		// S3C2440X Developer Notice (page 4) warns against writing a 1 to a 0 bit in the INTPND register.
		if (s2440INT->rINTPND & BIT_EINT0) s2440INT->rINTPND = BIT_EINT0;
		s2440INT->rINTMSK &= ~BIT_EINT0;
		s2440INT->rSRCPND = BIT_EINT2;
		// S3C2440X Developer Notice (page 4) warns against writing a 1 to a 0 bit in the INTPND register.
		if (s2440INT->rINTPND & BIT_EINT2) s2440INT->rINTPND = BIT_EINT2;
		s2440INT->rINTMSK &= ~BIT_EINT2;
		break;

	case SYSINTR_CAM:
		s2440INT->rSUBSRCPND = INTSUB_CAM_P;
		s2440INT->rSUBSRCPND = INTSUB_CAM_C;
		s2440INT->rSRCPND = BIT_CAM;

		if (s2440INT->rINTPND & BIT_CAM)
		{
			s2440INT->rINTPND = BIT_CAM;
		}
		s2440INT->rINTSUBMSK &= ~(INTSUB_CAM_P | INTSUB_CAM_C);
		s2440INT->rINTMSK &= ~BIT_CAM;
		break;

    case SYSINTR_IIC:
        s2440INT->rINTMSK &= ~BIT_IIC;
       break;

	}
    INTERRUPTS_ON();	
}


//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
BOOL 
OEMGetExtensionDRAM(
    LPDWORD lpMemStart, 
    LPDWORD lpMemLen
    ) 
{
    return FALSE; // no extension DRAM
}


//------------------------------------------------------------------------------
//
//  OEMQueryPerformanceCounter
//  
//      The OEMQueryPerformanceCounter function retrieves the current value of 
//      the high-resolution performance counter, if one exists. 
//  
//  BOOL QueryPerformanceCounter(
//  
//      LARGE_INTEGER  *lpliPerformanceCount    // address of current counter value
//     );   
//  
//  Parameters
//  
//  lpliPerformanceCount
//  
//      Points to a variable that the function sets, in counts, to the current 
//      performance-counter value. If the installed hardware does not support 
//      a high-resolution performance counter, this parameter can be to zero. 
//  
//  Return Value
//  
//      If the installed hardware supports a high-resolution performance 
//      counter, the return value is TRUE.
//      If the installed hardware does not support a high-resolution 
//      performance counter, the return value is FALSE.   
//  
//  If this function is implemented by the OEM, the pointer pQueryPerformanceCounter
//  should be initialized as follows:
//  
//  BOOL (*pQueryPerformanceCounter)(LARGE_INTEGER *lpliPerformanceCount)=OEMQueryPerformanceCounter;
//
//------------------------------------------------------------------------------
BOOL 
OEMQueryPerformanceCounter(
    LARGE_INTEGER *lpliPerformanceCount
    )
{
    extern DWORD PerfCountSinceTick();
    
    ULARGE_INTEGER liBase;
    DWORD dwCurCount;

	// Make sure CurTicks is the same before and after read of counter to account for
	// possible rollover
    do {
        liBase = CurTicks;
        dwCurCount = PerfCountSinceTick();
    } while  (liBase.LowPart != CurTicks.LowPart) ;  

    lpliPerformanceCount->QuadPart = liBase.QuadPart + dwCurCount;
    
    return TRUE;
}



//------------------------------------------------------------------------------
//
//  OEMQueryPerformanceFrequency
//  
//      The OEMQueryPerformanceFrequency function retrieves the frequency of 
//      the high-resolution performance counter, if one exists. 
//  
//  BOOL OEMQueryPerformanceFrequency(
//  
//      LARGE_INTEGER  *lpliPerformanceFreq     // address of current frequency
//     );   
//  
//  Parameters
//  
//  lpliPerformanceFreq
//  
//      Points to a variable that the function sets, in counts per second, to 
//      the current performance-counter frequency. If the installed hardware 
//      does not support a high-resolution performance counter, this parameter
//      can be to zero. 
//  
//  Return Value
//  
//      If the installed hardware supports a high-resolution performance 
//      counter, the return value is TRUE.
//      If the installed hardware does not support a high-resolution 
//      performance counter, the return value is FALSE.
//  
//  If this function is implemented by the OEM, the pointer pQueryPerformanceFrequency
//  should be initialized as follows:
//  
//  BOOL (*pQueryPerformanceFrequency)(LARGE_INTEGER *lpPerformanceFrequency)=OEMQueryPerformanceFrequency;
//
//------------------------------------------------------------------------------
BOOL 
OEMQueryPerformanceFrequency(
    LARGE_INTEGER *lpliPerformanceFreq
    ) 
{
    extern DWORD PerfCountFreq();
    
    lpliPerformanceFreq->HighPart = 0;
    lpliPerformanceFreq->LowPart  = PerfCountFreq();
    return TRUE;
}

// set pointers to OEM functions
BOOL (*pQueryPerformanceCounter)(LARGE_INTEGER *lpliPerformanceCount)=OEMQueryPerformanceCounter;
BOOL (*pQueryPerformanceFrequency)(LARGE_INTEGER *lpliPerformanceFreq)=OEMQueryPerformanceFrequency;


//
// CPU-specific functions for OEMIdle
//
extern void  CPUEnterIdle(DWORD dwIdleParam);
extern DWORD CPUGetSysTimerCountMax(DWORD dwIdleMSecRequested);
extern void  CPUSetSysTimerCount(DWORD dwIdleMSec);
extern BOOL CPUClearSysTimerIRQ(void);


//
// dougfir or later
//
extern DWORD
CPUGetSysTimerCountElapsed(
    DWORD dwTimerCountdownMSec,
    volatile DWORD *pCurMSec,
    DWORD *pPartialCurMSec,
    volatile ULARGE_INTEGER *pCurTicks
    );

//------------------------------------------------------------------------------
//
//  This routine is called by the kernel when there are no threads ready to
//  run. The CPU should be put into a reduced power mode and halted. It is 
//  important to be able to resume execution quickly upon receiving an interrupt.
//  Note: It is assumed that interrupts are off when OEMIdle is called.  Interrrupts
//  are turned off when OEMIdle returns.
//
//------------------------------------------------------------------------------
static DWORD dwPartialCurMSec = 0;		// Keep CPU-specific sub-millisecond leftover.
void
OEMIdle( DWORD dwIdleParam )
{
	DWORD dwIdleMSec;
	DWORD dwPrevMSec = *pCurMSec;

	// Use for 64-bit math
	ULARGE_INTEGER currIdle = { curridlelow, curridlehigh };

	if ((int) (dwIdleMSec = dwReschedTime - dwPrevMSec) <= 0) 
	{
		return;				// already time to wakeup
	}

	// just idle till tick if profiling or running iltiming
	if (bProfileTimerRunning || fIntrTime)	// fIntrTime : Interrupt Latency timeing.
	{
		// idle till end of 'tick'
		CPUEnterIdle(dwIdleParam);

		// Update global idle time and return
		currIdle.QuadPart += RESCHED_PERIOD;
		curridlelow = currIdle.LowPart;
		curridlehigh = currIdle.HighPart;
        
		return;
	}

	//
	// Since OEMIdle( ) is being called in the middle of a normal reschedule
	// period, CurMSec, dwPartialCurMSec, and CurTicks need to be updated accordingly.
	// Once we reach this point, we must re-program the timer (if we ever did) 
	// because dwPartialCurMSec will be modified in the next function call.
	//
	CPUGetSysTimerCountElapsed(RESCHED_PERIOD, pCurMSec, &dwPartialCurMSec, pCurTicks);

	if ((int) (dwIdleMSec -= *pCurMSec - dwPrevMSec) > 0)
	{
		dwPrevMSec = *pCurMSec;

		//
		// The system timer may not be capable of arbitrary timeouts. Get the
		// CPU-specific highest possible timeout available.
		//
		dwIdleMSec = CPUGetSysTimerCountMax(dwIdleMSec);

		//
		// Set the timer to wake up much later than usual, if needed.
		//
		CPUSetSysTimerCount(dwIdleMSec);
		CPUClearSysTimerIRQ( );

		//
		// Enable wakeup on any interrupt, then go to sleep.
		//
//		DEBUGMSG(1, (TEXT("OEMIDle  \r\n")));
		CPUEnterIdle(dwIdleParam);
		INTERRUPTS_OFF( );
        
		//
		// We're awake! The wake-up ISR (or any other ISR) has already run.
		//
		if (dwPrevMSec != *pCurMSec)
		{
			//
			// We completed the full period we asked to sleep.  Update the counters.
			//
			*pCurMSec  += (dwIdleMSec - RESCHED_PERIOD); // Subtract resched period, because ISR also incremented.
			CurTicks.QuadPart += (dwIdleMSec - RESCHED_PERIOD) * dwReschedIncrement;

			currIdle.QuadPart += dwIdleMSec;
		} else {
			//
			// Some other interrupt woke us up before the full idle period was
			// complete.  Determine how much time has elapsed.
			//
			currIdle.QuadPart += CPUGetSysTimerCountElapsed(dwIdleMSec, pCurMSec, &dwPartialCurMSec, pCurTicks);
		}
	}

	// Re-arm counters
	CPUSetSysTimerCount(RESCHED_PERIOD);
	CPUClearSysTimerIRQ( );

	// Update global idle time
	curridlelow = currIdle.LowPart;
	curridlehigh = currIdle.HighPart;

	return;
}

//------------------------------------------------------------------------------
//
//  DWORD GetTickCount(VOID)    Return count of time since boot in milliseconds
//
//------------------------------------------------------------------------------
DWORD 
SC_GetTickCount(void) 
{
	DWORD dwInc = 0, dwPartial = dwPartialCurMSec;
	DWORD curReturnMSec;
	ULARGE_INTEGER cdummy = {0, 0};

	curReturnMSec=*pCurMSec;
	CPUGetSysTimerCountElapsed(RESCHED_PERIOD, &dwInc, &dwPartial, &cdummy);

	return (curReturnMSec==*pCurMSec)?curReturnMSec+dwInc:*pCurMSec;
}


volatile BOOL fResumeFlag;
extern void CPUEnterIdleMode(void);

//------------------------------------------------------------------------------
// Initialize SDMMC block.. 
//------------------------------------------------------------------------------
static void InitSDMMC(void) 
{
	volatile IOPreg *s2440IOP = (IOPreg *)IOP_BASE;

	// Initialize SDMMC and Configure SDMMC Card Detect
	// GPIO Configure 
	// RETAILMSG(1,(TEXT("SDMMC config current rGPGCON: %x\r\n"), s2440IOP->rGPGCON));  
	// We must need this PULL-UP routines to inialize.
	// s2440IOP->rGPGUP = 0xF800;   
#if SDIO_FOR_100BD		// for b'd revision 1.00
	// s2440IOP->rGPGUP &= ~(1<<10);
	s2440IOP->rGPGUP = 0xF800;
	s2440IOP->rGPGCON &= ~((0x3 << 20));   
	s2440IOP->rGPGCON |=  ((0x2 << 20));			// External Interrupt #18 Enable
	//RETAILMSG(1,(TEXT("SDMMC config set rGPGCON: %x\r\n"), s2440IOP->rGPGCON));   
	s2440IOP->rEXTINT2 &= ~(0x7 << 8);			// Configure EINT18 as Both Edge Mode
	s2440IOP->rEXTINT2 |=  (0x7 << 8);
#else						// for b'd revision 0.17
	s2440IOP->rGPGUP = 0xF800;   
	s2440IOP->rGPGCON &= ~((0x3 << 16));   
	s2440IOP->rGPGCON |=  ((0x2 << 16));		/* External Interrupt #16 Enable				*/
	RETAILMSG(1,(TEXT("SDMMC config set rGPGCON: %x\r\n"), s2440IOP->rGPGCON));   
	s2440IOP->rEXTINT2 &= ~(0x7 << 0);			/* Configure EINT16 as Both Edge Mode		*/
	s2440IOP->rEXTINT2 |=  (0x0 << 0);			// low level trig
#endif
	/* Configure SDMMC Write Protect */
	s2440IOP->rGPHUP = 0x0;   
	s2440IOP->rGPHCON &= ~((0x3 << 16));   
	s2440IOP->rGPHCON |=  ((0x0 << 16));		/* GPH8/UCLK Write Protect Pin					*/
	//RETAILMSG(1,(TEXT("SDMMC config Init Done.\r\n"))); 
	  
	/* Enable USB Device */
	s2440IOP->rGPGCON &= ~((0x3 << 24));   
	s2440IOP->rGPGCON |=  ((0x1 << 24));		/* GPG9 Output Enable				*/
	s2440IOP->rGPGDAT |= ((1 << 12));	
}

//------------------------------------------------------------------------------
// Initialize and test the LCD block.. 
//------------------------------------------------------------------------------

/*
// Define some values for TFT 16bpp
#if(LCDTYPE == TFT16BPP)    // TFT 640*480 / 16bpp
#define FR_WIDTH            240
#define FR_HEIGHT           320
#define PhysicalVmemSize    FR_HEIGHT*FR_WIDTH*LCDTYPE


struct FrameBuffer {
   unsigned short pixel[FR_HEIGHT][FR_WIDTH];
};

#else if(LCDTYPE == STN8BPP)// STN 320*240 / 8bpp
#define FR_WIDTH            320
#define FR_HEIGHT           240
#define PhysicalVmemSize    FR_HEIGHT*FR_WIDTH

struct FrameBuffer {
   unsigned char pixel[FR_HEIGHT][FR_WIDTH];
};
#endif
*/

struct FrameBuffer {
	unsigned short pixel[LCD_YSIZE_TFT][LCD_XSIZE_TFT];
};
struct FrameBuffer *FBuf;


static void InitDisplay()
{
	int i, j;
	volatile IOPreg *s2440IOP;
	volatile LCDreg *s2440LCD;    

	s2440IOP = (IOPreg *)IOP_BASE;
	s2440LCD = (LCDreg *)LCD_BASE; 

//	LCD port initialize.
	s2440IOP->rGPCUP  = 0xFFFFFFFF;
	s2440IOP->rGPCCON = 0xAAAAAAAA;

	s2440IOP->rGPDUP  = 0xFFFFFFFF;
	s2440IOP->rGPDCON = 0xAAAAAAAA;

	s2440IOP->rGPGCON &= ~(3 << 8);					// Set LCD_PWREN as output
	s2440IOP->rGPGCON |=  (1 << 8);

	s2440IOP->rGPGDAT |=  (1 << 4);					// Backlight ON


	s2440LCD->rLCDCON1   =  (CLKVAL_TFT           <<  8) |   /* VCLK = HCLK / ((CLKVAL + 1) * 2) -> About 7 Mhz */  // ;;; SHL
				(MVAL_USED   <<  7) |   /* 0 : Each Frame                                   */
				(3           <<  5) |   /* TFT LCD Pannel                                   */
				(12          <<  1) |   /* 16bpp Mode                                       */
				(0           <<  0) ;   /* Disable LCD Output                               */

	s2440LCD->rLCDCON2   =  (VBPD        << 24) |   /* VBPD          :   1                              */
				(LINEVAL_TFT << 14) |   /* Virtical Size : 320 - 1                          */
				(VFPD        <<  6) |   /* VFPD          :   2                              */
				(VSPW        <<  0) ;   /* VSPW          :   1                              */

	s2440LCD->rLCDCON3   =  (HBPD        << 19) |   /* HBPD          :   6                              */
				(HOZVAL_TFT  <<  8) |   /* HOZVAL_TFT    : 240 - 1                          */
				(HFPD        <<  0) ;   /* HFPD          :   2                              */


	s2440LCD->rLCDCON4   =  (MVAL        <<  8) |   /* MVAL          :  13                              */
				(HSPW        <<  0) ;   /* HSPW          :   4                              */

	s2440LCD->rLCDCON5   =  (0           << 12) |   /* BPP24BL       : LSB valid                        */
				(1           << 11) |   /* FRM565 MODE   : 5:6:5 Format                     */
				(0           << 10) |   /* INVVCLK       : VCLK Falling Edge                */
				(1           <<  9) |   /* INVVLINE      : Inverted Polarity                */
				(1           <<  8) |   /* INVVFRAME     : Inverted Polarity                */
				(0           <<  7) |   /* INVVD         : Normal                           */
				(0           <<  6) |   /* INVVDEN       : Normal                           */
				(0           <<  5) |   /* INVPWREN      : Normal                           */
				(0           <<  4) |   /* INVENDLINE    : Normal                           */
				(1           <<  3) |   /* PWREN         : Disable PWREN                    */
				(0           <<  2) |   /* ENLEND        : Disable LEND signal              */
				(0           <<  1) |   /* BSWP          : Swap Disable                     */
				(1           <<  0) ;   /* HWSWP         : Swap Enable                      */

	s2440LCD->rLCDSADDR1 = ((FRAMEBUF_DMA_BASE >> 22)     << 21) |
					((M5D(FRAMEBUF_DMA_BASE >> 1)) <<  0);

	s2440LCD->rLCDSADDR2 = M5D((FRAMEBUF_DMA_BASE + (LCD_XSIZE_TFT * LCD_YSIZE_TFT * 2)) >> 1);

	s2440LCD->rLCDSADDR3 = (((LCD_XSIZE_TFT - LCD_XSIZE_TFT) / 1) << 11) | (LCD_XSIZE_TFT / 1);

	s2440LCD->rTCONSEL &= ~(0x7);	// ;;; SHL
	s2440LCD->rTPAL     = 0x0;
	s2440LCD->rLCDCON1 |= 1;

	memcpy((void *)FRAMEBUF_BASE, ScreenBitmap, ARRAY_SIZE_TFT_16BIT);
}

static void OEMInitInterrupts(void)	// for KITL 030828
{
	volatile INTreg *s2440INT = (INTreg *)INT_BASE;
	volatile IOPreg *s2440IOP = (IOPreg *)IOP_BASE;

	// Configure EINT9 for CS8900 interrupt.
	//
	s2440IOP->rGPGCON  = (s2440IOP->rGPGCON  & ~(0x3 << 0x2)) | (0x2 << 0x2);		// GPG1 == EINT9.
	s2440IOP->rGPGUP   = (s2440IOP->rGPGUP   |  (0x1 << 0x1));						// Disable pull-up.
	s2440IOP->rEXTINT1 = (s2440IOP->rEXTINT1 & ~(0xf << 0x4)) | (0x1 << 0x4);		// Level-high triggered.

	// Configure EINT8 for PD6710 interrupt.
	//
	s2440IOP->rGPGCON  = (s2440IOP->rGPGCON  & ~(0x3 << 0x0)) | (0x2 << 0x0);		// GPG0 == EINT8.
	s2440IOP->rGPGUP   = (s2440IOP->rGPGUP   |  (0x1 << 0x0));						// Disable pull-up.
	s2440IOP->rEXTINT1 = (s2440IOP->rEXTINT1 & ~(0xf << 0x0)) | (0x1 << 0x0);		// Level-high triggered.

	// Mask and clear all peripheral interrupts (these come through a second-level "GPIO" interrupt register).
	//
	s2440IOP->rEINTMASK = BIT_ALLMSK;	// Mask all EINT interrupts.
	s2440IOP->rEINTPEND = BIT_ALLMSK;	// Clear pending EINT interrupts.

	// Mask and clear all interrupts.
	//
	s2440INT->rINTMSK = BIT_ALLMSK;			// Mask all interrupts (reset value).
	s2440INT->rINTMSK &= ~BIT_BAT_FLT;
	s2440INT->rSRCPND = BIT_ALLMSK;			// Clear pending interrupts.
	s2440INT->rINTPND = s2440INT->rINTPND;		// S3C2440X developer notice (page 4) warns against writing a 1 to any
							// 0 bit field in the INTPND register.  Instead we'll write the INTPND value itself.
}


