//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this sample source code is subject to the terms of the Microsoft
// license agreement under which you licensed this sample source code. If
// you did not accept the terms of the license agreement, you are not
// authorized to use this sample source code. For the terms of the license,
// please see the license agreement between you and Microsoft or, if applicable,
// see the LICENSE.RTF on your install media or the root of your tools installation.
// THE SAMPLE SOURCE CODE IS PROVIDED "AS IS", WITH NO WARRANTIES.
//
/*++
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:  

Abstract:  
   This file implements the NK kernel interfaces for firmware interrupt
  support on the CEPC.
 
Functions:


Notes: 

--*/

#include <windows.h>
#include <oal.h>
#include <nkintr.h>
#include <x86boot.h>

#include "pci.h"



/*
    Global:  dwOEMDrWatsonSize

    Global variable which specify DrWatson buffer size. It can be fixed
    in config.bib via FIXUPVAR.
 */
#define DR_WATSON_SIZE_NOT_FIXEDUP (-1)
DWORD dwOEMDrWatsonSize = DR_WATSON_SIZE_NOT_FIXEDUP;

/*
    @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 PCIInitBusInfo (void);
DWORD OEMPowerManagerInit(void);
void InitClock(void);
void x86InitMemory (void);
static void x86InitRomChain (void);
void x86InitPICs(void);


extern ULONG
PCIWriteBusData(
              IN ULONG BusNumber,
              IN ULONG DeviceNumber,
              IN ULONG FunctionNumber,
              IN PVOID Buffer,
              IN ULONG Offset,
              IN ULONG Length
              );
extern ULONG
PCIReadBusData(
              IN ULONG BusNumber,
              IN ULONG DeviceNumber,
              IN ULONG FunctionNumber,
              IN PVOID Buffer,
              IN ULONG Offset,
              IN ULONG Length
              );

void disLegacyUsbSupport(void);
// LPC interface id
#define INTEL_LPC_DEVICEID_ICH2   0x2440
#define INTEL_LPC_DEVICEID_ICH2M  0x244C
#define INTEL_LPC_DEVICEID_ICH4   0x24C0
#define INTEL_LPC_DEVICEID_ICH4M  0x24CC
#define INTEL_LPC_DEVICEID_ICH6ME 0x2641
#define INTEL_LPC_DEVICEID_ICH7   0x27B8
#define INTEL_LPC_DEVICEID_ICH7M  0x27B9
#define INTEL_LPC_DEVICEID_ICH7MBD  0x27BD
#define INTEL_LPC_DEVICEID_ICH8   0x2810
#define INTEL_LPC_DEVICEID_ICH8M  0x2815
#define INTEL_LPC_DEVICEID_ICH8DH 0x2812
#define INTEL_LPC_DEVICEID_ICH8DO 0x2814
#define INTEL_LPC_DEVICEID_ICH8ME 0x2811

#define INTEL_LPC_DEVICEID_US15W  0x8119
#define INTEL_LPC_DEVICEID_DIAMONDVILLE  0x27B9

#define INTEL_LPC_DEVICEID_ICH9DH 0x2912
#define INTEL_LPC_DEVICEID_ICH9DO 0x2914
#define INTEL_LPC_DEVICEID_ICH9R  0x2916
#define INTEL_LPC_DEVICEID_ICH9ME 0x2917
#define INTEL_LPC_DEVICEID_ICH9   0x2918
#define INTEL_LPC_DEVICEID_ICH9M  0x2919

#define INTEL_LPC_DEVICEID_ICH10  0x3A10
#define INTEL_LPC_DEVICEID_ICH10D 0x3A14


BOOL g_fPostInit;

void OEMInit()
{   
    // Set up the debug zones according to the fix-up variable initialOALLogZones
    OALLogSetZones(initialOALLogZones);

    OALMSG(OAL_FUNC, (L"+OEMInit\r\n"));

    // initialize interrupts
    OALIntrInit ();

    // initialize PIC
    x86InitPICs();

    // Initialize PCI bus information
    PCIInitBusInfo ();

    // starts KITL (will be a no-op if KITLDLL doesn't exist)
    KITLIoctl (IOCTL_KITL_STARTUP, NULL, 0, NULL, 0, NULL);
    
#ifdef DEBUG

    // 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.    
    NKOutputDebugString (TEXT("Firmware Init\r\n"));
    DEBUGMSG(1,(TEXT("Built %s %s\r\n"),TEXT(__DATE__),TEXT(__TIME__) ));
#endif

    OEMPowerManagerInit();
    
    // initialize clock
    InitClock();

    // initialize memory (detect extra ram, MTRR/PAT etc.)
    x86InitMemory ();

    // Reserve 128kB memory for Watson Dumps
    dwNKDrWatsonSize = 0;
    if (dwOEMDrWatsonSize != DR_WATSON_SIZE_NOT_FIXEDUP) {
        dwNKDrWatsonSize = dwOEMDrWatsonSize;
    }

    x86InitRomChain();

    // forces the operating system (OS) to boot from a clean object store file system
    NKForceCleanBoot();

    // initialize UHCI/EHCI controller to overcome Legacy USB support
    disLegacyUsbSupport();


#ifdef DEBUG
    {
        DWORD data;
        PCIReadBusData(0,31,0,&data,0x60,4);
        DEBUGMSG(1,(TEXT("INTA-D Routing @ 60 = %x\r\n"),data));
        PCIReadBusData(0,31,0,&data,0x68,4);
        DEBUGMSG(1,(TEXT("INTE-H Routing @ 68 = %x\r\n"),data));
        DEBUGMSG(1,(TEXT("4D0-1 1:%x 0:%x\r\n"),READ_PORT_UCHAR((PUCHAR)0x4d1),READ_PORT_UCHAR((PUCHAR)0x4d0) ));
    }
#endif // DEBUG


#ifdef DEBUG
    NKOutputDebugString (TEXT("Firmware Init Done.\r\n"));
#endif
    
    // Override default (100 milliseconds) to 10 ms.
    g_pOemGlobal->dwDefaultThreadQuantum = 10; // 10 milliseconds
    
    OALMSG(OAL_FUNC, (L"-OEMInit\r\n"));
}

BOOL x86IoCtlPostInit (
    UINT32 code, VOID *lpInBuf, UINT32 nInBufSize, VOID *lpOutBuf, 
    UINT32 nOutBufSize, UINT32 *lpBytesReturned
) {

    // Critical sections to be initialized
    extern CRITICAL_SECTION RTC_critsect;

    if (lpBytesReturned) {
        lpBytesReturned = 0;
    }

    // Initialize critical sections
    InitializeCriticalSection(&RTC_critsect);
    g_fPostInit = TRUE;

    return TRUE;
}

#include "pehdr.h"
#include "romldr.h"
#include "romxip.h"

/*
  Initialize the rom chain
*/

#define NOT_FIXEDUP        (DWORD*)-1

DWORD *pdwXIPLoc = NOT_FIXEDUP;

static void x86InitRomChain (void)
{
    // Added for MultiXIP stuff
    static      ROMChain_t  s_pNextRom[MAX_ROM] = {0};
    DWORD       dwRomCount = 0;
    DWORD       dwChainCount = 0;
    DWORD *     pdwCurXIP;
    DWORD       dwNumXIPs;
    PXIPCHAIN_ENTRY pChainEntry = NULL;

#ifdef DEBUG
    TCHAR       szXIPName[XIP_NAMELEN];
    int         i;  // loop ctr
#endif

#ifdef DEBUG
    NKOutputDebugString (TEXT("Looking for rom chain\n"));
#endif

    if(pdwXIPLoc == NOT_FIXEDUP){
#ifdef DEBUG
        NKOutputDebugString (TEXT("Rom chain NOT found\n"));
#endif
        return;  // no chain or not fixed up properly
    }

#ifdef DEBUG
    NKOutputDebugString (TEXT("Rom chain found\n"));
#endif


    // 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){
      NKOutputDebugString (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;

#ifdef DEBUG
            NKOutputDebugString ( _T("XIP found: ") );

            for (i = 0; (i< (XIP_NAMELEN-1)) && (pChainEntry->szName[i] != '\0'); ++i)
            {
                szXIPName[i] = (TCHAR)pChainEntry->szName[i];
            }
            szXIPName[i] = TEXT('\0');
            NKOutputDebugString ( szXIPName );

            NKOutputDebugString ( _T("\n") );
#endif

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

        ++pChainEntry;
        dwChainCount++;
    }

#ifdef DEBUG
{
  ROMChain_t         *pchain = OEMRomChain;

  NKOutputDebugString ( _T("chain contents...\n") );
  while(pchain){
    NKOutputDebugString ( _T("found item\n") );
    pchain = pchain->pNext;
  }
}
#endif
    
}


static void millisecondDelay(int iDelay)
{
    ULONG dwCount = (ULONG)(iDelay * 1500);
    ULONG dwIndex;
    UCHAR temp;

    for (dwIndex = 0; dwIndex < dwCount; dwIndex++)
    {
        temp = INPORT8((PUCHAR)0x80);
    }
}


void disLegacyUsbSupport(void)
{
    USHORT deviceId;
    ULONG  pmBase;
    ULONG  data;
    ULONG  usb1Addr, usb2Addr, usb3Addr;
	ULONG  usb4Addr, usb5Addr, usb6Addr;

	/* Begin Disble NATIVE PCI of Cantiga SATA controller */
    PCIReadBusData(0x0, 0x1f, 0x2, &deviceId,0x02, 2); //should be 2928 //
	
	RETAILMSG(1,(TEXT("ide deviceId = 0x%x\r\n"),deviceId));
	if( deviceId == 0x2928){
		data = 0x8A;
		PCIWriteBusData(0x0, 0x1f, 0x2, &(PVOID)data, 0x9, 1);
	}
	/* End   Disble NATIVE PCI of Cantiga SATA controller */
	
    PCIReadBusData(0x0, 0x1f, 0x0, &deviceId,0x02, 2);
	RETAILMSG(1,(TEXT("disLegacyUsbSupport LPC deviceId = 0x%x\r\n"),deviceId));

    PCIReadBusData(0x0, 0x1f, 0x0, &pmBase, 0x40, 4);
    pmBase &= 0xffffff80;
    
    if(deviceId == INTEL_LPC_DEVICEID_ICH6ME)
    {
        // 915

        data = 0x01;
        PCIWriteBusData(0x0, 0x1d, 0x7, &(PVOID)data, 0x6B, 1);
        data = 0xe0;
        PCIWriteBusData(0x0, 0x1d, 0x7, &(PVOID)data, 0x6F, 1);

        data = 0x0000;
        PCIWriteBusData(0x0, 0x1d, 0x7, &(PVOID)data, 0x6C, 2);
        PCIWriteBusData(0x0, 0x1d, 0x7, &(PVOID)data, 0x70, 2);

        data = INPORT32((PULONG)(pmBase + 0x30));
        data &= 0xfff9fff7;
        OUTPORT32((PULONG)(pmBase + 0x30), data);


        // Get USB1/2/3 base addresses
        PCIReadBusData(0x0, 0x1d, 0x0, &usb1Addr, 0x20, 4);
        usb1Addr &= 0xffffffe0;
        PCIReadBusData(0x0, 0x1d, 0x1, &usb2Addr, 0x20, 4);
        usb2Addr &= 0xffffffe0;
        PCIReadBusData(0x0, 0x1d, 0x2, &usb3Addr, 0x20, 4);
        usb3Addr &= 0xffffffe0;

        //  Clear the run/stop bit (bit 0) and cf flag (bit 6) in each USB CMD register.
        data = INPORT16((PUSHORT)(usb1Addr + 0));
        data &= 0xffbe;
        OUTPORT16((PUSHORT)(usb1Addr + 0), data);
        data = INPORT16((PUSHORT)(usb2Addr + 0));
        data &= 0xffbe;
        OUTPORT16((PUSHORT)(usb2Addr + 0), data);
        data = INPORT16((PUSHORT)(usb3Addr + 0));
        data &= 0xffbe;
        OUTPORT16((PUSHORT)(usb3Addr + 0), data);

        millisecondDelay(1);

        // Disable SMI generation by clearing USB controller's register C0 bit 4 (USBSMIEN), enable
        // interrupt generation by setting the USB controller's register C0 bit 13 (USBPIRQEN), and
        // disable SMI sources.
        data = 0x00;
        PCIWriteBusData(0x0, 0x1d, 0x0, &(PVOID)data, 0xC0, 1);
        data = 0xff;
        PCIWriteBusData(0x0, 0x1d, 0x0, &(PVOID)data, 0xC1, 1);
        data = 0x00;
        PCIWriteBusData(0x0, 0x1d, 0x1, &(PVOID)data, 0xC0, 1);
        data = 0xff;
        PCIWriteBusData(0x0, 0x1d, 0x1, &(PVOID)data, 0xC1, 1);
        data = 0x00;
        PCIWriteBusData(0x0, 0x1d, 0x2, &(PVOID)data, 0xC0, 1);
        data = 0xff;
        PCIWriteBusData(0x0, 0x1d, 0x2, &(PVOID)data, 0xC1, 1);
    }    
    else if(
		(deviceId == INTEL_LPC_DEVICEID_DIAMONDVILLE) 
    	||(deviceId == INTEL_LPC_DEVICEID_ICH7)||(deviceId == INTEL_LPC_DEVICEID_ICH7M)
		)
    {
    	// 945...
        data = INPORT32((PULONG)(pmBase + 0x30));
        data &= 0xfffffff7; // disable LEGACY_USB_EN
        OUTPORT32((PULONG)(pmBase + 0x30), data);
    }
    else if(deviceId == INTEL_LPC_DEVICEID_US15W)
    {
        // Get USB1/2/3 base addresses
        PCIReadBusData(0x0, 0x1d, 0x0, &usb1Addr, 0x20, 4);
        usb1Addr &= 0xffffffe0;
        PCIReadBusData(0x0, 0x1d, 0x1, &usb2Addr, 0x20, 4);
        usb2Addr &= 0xffffffe0;

        //  Clear the run/stop bit (bit 0) and cf flag (bit 6) in each USB CMD register.
        data = INPORT16((PUSHORT)(usb1Addr + 0));
        data &= 0xffbe;
        OUTPORT16((PUSHORT)(usb1Addr + 0), data);
        data = INPORT16((PUSHORT)(usb2Addr + 0));
        data &= 0xffbe;
        OUTPORT16((PUSHORT)(usb2Addr + 0), data);
	}		
	else if(deviceId == INTEL_LPC_DEVICEID_ICH9ME)
	{
        data = 0x01;
        PCIWriteBusData(0x0, 0x1d, 0x7, &(PVOID)data, 0x6B, 1);
        data = 0xe0;
        PCIWriteBusData(0x0, 0x1d, 0x7, &(PVOID)data, 0x6F, 1);

        data = 0x0000;
        PCIWriteBusData(0x0, 0x1d, 0x7, &(PVOID)data, 0x6C, 2);
        PCIWriteBusData(0x0, 0x1d, 0x7, &(PVOID)data, 0x70, 2);

        data = 0x01;
        PCIWriteBusData(0x0, 0x1a, 0x7, &(PVOID)data, 0x6B, 1);
        data = 0xe0;
        PCIWriteBusData(0x0, 0x1a, 0x7, &(PVOID)data, 0x6F, 1);

        data = 0x0000;
        PCIWriteBusData(0x0, 0x1a, 0x7, &(PVOID)data, 0x6C, 2);
        PCIWriteBusData(0x0, 0x1a, 0x7, &(PVOID)data, 0x70, 2);
#if 0		
        data = INPORT32((PULONG)(pmBase + 0x30));
        data &= 0xfffffff7; // disable LEGACY_USB_EN
        OUTPORT32((PULONG)(pmBase + 0x30), data);
#endif
        // Get USB UHCI base addresses
        PCIReadBusData(0x0, 0x1d, 0x0, &usb1Addr, 0x20, 4);
        usb1Addr &= 0xffffffe0;
        PCIReadBusData(0x0, 0x1d, 0x1, &usb2Addr, 0x20, 4);
        usb2Addr &= 0xffffffe0;
        PCIReadBusData(0x0, 0x1d, 0x2, &usb3Addr, 0x20, 4);
        usb3Addr &= 0xffffffe0;
        PCIReadBusData(0x0, 0x1d, 0x3, &usb4Addr, 0x20, 4);
        usb3Addr &= 0xffffffe0;
        PCIReadBusData(0x0, 0x1a, 0x0, &usb5Addr, 0x20, 4);
        usb1Addr &= 0xffffffe0;
        PCIReadBusData(0x0, 0x1a, 0x1, &usb6Addr, 0x20, 4);
        usb2Addr &= 0xffffffe0;

        //  Clear the run/stop bit (bit 0) and cf flag (bit 6) in each USB CMD register.
        data = INPORT16((PUSHORT)(usb1Addr + 0));
        data &= 0xffbe;
        OUTPORT16((PUSHORT)(usb1Addr + 0), data);
        data = INPORT16((PUSHORT)(usb2Addr + 0));
        data &= 0xffbe;
        OUTPORT16((PUSHORT)(usb2Addr + 0), data);
        data = INPORT16((PUSHORT)(usb3Addr + 0));
        data &= 0xffbe;
        OUTPORT16((PUSHORT)(usb3Addr + 0), data);
        data = INPORT16((PUSHORT)(usb4Addr + 0));
        data &= 0xffbe;
        OUTPORT16((PUSHORT)(usb4Addr + 0), data);

        data = INPORT16((PUSHORT)(usb5Addr + 0));
        data &= 0xffbe;
        OUTPORT16((PUSHORT)(usb4Addr + 0), data);
        data = INPORT16((PUSHORT)(usb6Addr + 0));
        data &= 0xffbe;
        OUTPORT16((PUSHORT)(usb4Addr + 0), data);
        
       
	}			
    else if(
		(deviceId == INTEL_LPC_DEVICEID_ICH10)||(deviceId == INTEL_LPC_DEVICEID_ICH10D)
		)
    {
    	//G45
        data = INPORT32((PULONG)(pmBase + 0x30));
        data &= 0xfffffff7; // disable LEGACY_USB_EN
        OUTPORT32((PULONG)(pmBase + 0x30), data);

        // Get USB UHCI base addresses
        PCIReadBusData(0x0, 0x1d, 0x0, &usb1Addr, 0x20, 4);
        usb1Addr &= 0xffffffe0;
        PCIReadBusData(0x0, 0x1d, 0x1, &usb2Addr, 0x20, 4);
        usb2Addr &= 0xffffffe0;
        PCIReadBusData(0x0, 0x1d, 0x2, &usb3Addr, 0x20, 4);
        usb3Addr &= 0xffffffe0;
        PCIReadBusData(0x0, 0x1a, 0x0, &usb4Addr, 0x20, 4);
        usb1Addr &= 0xffffffe0;
        PCIReadBusData(0x0, 0x1a, 0x1, &usb5Addr, 0x20, 4);
        usb2Addr &= 0xffffffe0;
        PCIReadBusData(0x0, 0x1a, 0x2, &usb6Addr, 0x20, 4);
        usb3Addr &= 0xffffffe0;

        //  Clear the run/stop bit (bit 0) and cf flag (bit 6) in each USB CMD register.
        data = INPORT16((PUSHORT)(usb1Addr + 0));
        data &= 0xffbe;
        OUTPORT16((PUSHORT)(usb1Addr + 0), data);
        data = INPORT16((PUSHORT)(usb2Addr + 0));
        data &= 0xffbe;
        OUTPORT16((PUSHORT)(usb2Addr + 0), data);
        data = INPORT16((PUSHORT)(usb3Addr + 0));
        data &= 0xffbe;
        OUTPORT16((PUSHORT)(usb3Addr + 0), data);
        data = INPORT16((PUSHORT)(usb4Addr + 0));
        data &= 0xffbe;
        OUTPORT16((PUSHORT)(usb4Addr + 0), data);
        data = INPORT16((PUSHORT)(usb5Addr + 0));
        data &= 0xffbe;
        OUTPORT16((PUSHORT)(usb5Addr + 0), data);
        data = INPORT16((PUSHORT)(usb6Addr + 0));
        data &= 0xffbe;
        OUTPORT16((PUSHORT)(usb6Addr + 0), data);

        millisecondDelay(1);

        data = 0x00;
        PCIWriteBusData(0x0, 0x1d, 0x0, &(PVOID)data, 0xC0, 1);
        data = 0xff;
        PCIWriteBusData(0x0, 0x1d, 0x0, &(PVOID)data, 0xC1, 1);
        data = 0x00;
        PCIWriteBusData(0x0, 0x1d, 0x1, &(PVOID)data, 0xC0, 1);
        data = 0xff;
        PCIWriteBusData(0x0, 0x1d, 0x1, &(PVOID)data, 0xC1, 1);
        data = 0x00;
        PCIWriteBusData(0x0, 0x1d, 0x2, &(PVOID)data, 0xC0, 1);
        data = 0xff;
        PCIWriteBusData(0x0, 0x1d, 0x2, &(PVOID)data, 0xC1, 1);
	}	
	else 
    {
		// do nothing
    }
}

