//-----------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation.  All rights reserved.
//
//
// Use of this source code is subject to the terms of the Microsoft end-user
// license agreement (EULA) under which you licensed this SOFTWARE PRODUCT.
// If you did not accept the terms of the EULA, you are not authorized to use
// this source code. For a copy of the EULA, please see the LICENSE.RTF on your
// install media.
//
//-----------------------------------------------------------------------------
//
//  Copyright (C) 2004-2007, Freescale Semiconductor, Inc. All Rights Reserved.
//  THIS SOURCE CODE, AND ITS USE AND DISTRIBUTION, IS SUBJECT TO THE TERMS
//  AND CONDITIONS OF THE APPLICABLE LICENSE AGREEMENT
//
//-----------------------------------------------------------------------------
//
//  File:  kitl.c
//
//  Support routines for KITL.
//
//  Note: These routines are stubbed out in the kern image.
//
//-----------------------------------------------------------------------------
#include <bsp.h>
#include <kitl_cfg.h>
#include <debugserial.h>
#include <devload.h>

extern DWORD g_dwKITLThreadPriority;

//-----------------------------------------------------------------------------

#define BSP_PREV_ARGS_COOKIE    0x8C020800
#define BSP_PREV_ARGS_MAC       0x8C02080C

#define USE_DHCP_RENEW          1

// #define KITL_USE_POLLING_MODE      // Uncomment to enable polled KITL mode.
#define KITL_THREAD_HIGH_PRIORITY 131 // Default KITL thread priority is 131.

//-----------------------------------------------------------------------------

static PCSP_EPIT_REG g_pEPIT;
static UINT32 g_nCountsPerMsec;

//------------------------------------------------------------------------------
// Prototypes for SMSC LAN911x

extern BOOL   LAN911xInit(UINT8 *pAddress, UINT32 offset, UINT16 mac[3]);
extern UINT16 LAN911xSendFrame(UINT8 *pBuffer, UINT32 length);
extern UINT16 LAN911xGetFrame(UINT8 *pBuffer, UINT16 *pLength);
extern VOID   LAN911xEnableInts();
extern VOID   LAN911xDisableInts();
extern VOID   LAN911xCurrentPacketFilter(UINT32 filter);
extern BOOL   LAN911xMulticastList(UINT8 *pAddresses, UINT32 count);

#define OAL_ETHDRV_LAN911x  { \
    LAN911xInit, NULL, NULL, LAN911xSendFrame, LAN911xGetFrame, \
    LAN911xEnableInts, LAN911xDisableInts, \
    NULL, NULL,  LAN911xCurrentPacketFilter, LAN911xMulticastList \
}

static OAL_KITL_ETH_DRIVER g_kitlEthLan911x = OAL_ETHDRV_LAN911x;

static OAL_KITL_SERIAL_DRIVER g_kitlCspSerial =
{
    SerialInit,
    SerialDeinit,
    SerialSend,
    SerialSendComplete,
    SerialRecv,
    SerialEnableInts,
    SerialDisableInts,
    NULL,
    NULL,
    NULL
};

static OAL_KITL_DEVICE g_kitlDevices[] = {
    {
        L"LAN911x", Internal, BSP_BASE_REG_PA_LAN911x_IOBASE, 0,
        OAL_KITL_TYPE_ETH, &g_kitlEthLan911x
    },
    {
        L"CSPSERIAL", Internal, BSP_BASE_REG_PA_SERIALKITL, 0,
        OAL_KITL_TYPE_SERIAL, &g_kitlCspSerial
    },
    {
        NULL, 0, 0, 0, 0, NULL
    }
};


//------------------------------------------------------------------------------
//
//  Function:  OALEnableKitlSerial
//
//  This function is called from other OAL to enable corresponding UART clock,
//  For Serial KITL
//
static void OALEnableKitlSerial(void)
{
    UINT32 uiMask;
    _OALKITLSharedDataStruct *pOALKITLSharedData =
        (_OALKITLSharedDataStruct *)(g_pOemGlobal->pKitlInfo);

    // Verify that OEMInit() has properly intialized the g_pCCM pointer
    // to provide access to the UART control registers.
    if (pOALKITLSharedData && pOALKITLSharedData->g_pCCM)
    {
        switch (BSP_BASE_REG_PA_SERIALKITL)
        {
            case CSP_BASE_REG_PA_UART1:
                uiMask = CCM_CGR0_UART1_MASK;
                SETREG32(&(pOALKITLSharedData->g_pCCM)->CGR[0], uiMask);
            break;

            case CSP_BASE_REG_PA_UART2:
                uiMask = CCM_CGR0_UART2_MASK;
                SETREG32(&(pOALKITLSharedData->g_pCCM)->CGR[0], uiMask);
            break;

            case CSP_BASE_REG_PA_UART3:
                uiMask = CCM_CGR1_UART3_MASK;
                SETREG32(&(pOALKITLSharedData->g_pCCM)->CGR[1], uiMask);
            break;

            case CSP_BASE_REG_PA_UART4:
                uiMask = CCM_CGR1_UART4_MASK;
                SETREG32(&(pOALKITLSharedData->g_pCCM)->CGR[1], uiMask);
            break;

            case CSP_BASE_REG_PA_UART5:
                uiMask = CCM_CGR1_UART5_MASK;
                SETREG32(&(pOALKITLSharedData->g_pCCM)->CGR[1], uiMask);
            break;
        }
    }
}


//------------------------------------------------------------------------------
// Platform entry point for KITL.
//
// Called when KITLIoctl (IOCTL_KITL_STARTUP, ...) is called.
//
BOOL OEMKitlStartup(void)
{
   BOOL rc;
   OAL_KITL_ARGS *pArgs, args;
   CHAR *szDeviceId, buffer[OAL_KITL_ID_SIZE];
   OAL_KITL_DEVICE *pKitlDevice = NULL;
   UINT32 *kitlFlags;
   UINT32 numKitlDevice = 0;
   BSP_ARGS *pBspArgs = (BSP_ARGS *)IMAGE_SHARE_ARGS_UA_START;

	dpCurSettings.ulZoneMask = 0;
#ifdef DEBUG
   // KITL gets its own debug zone and does not share
   // the OAL one any longer.
   //
   // Turn on all debug zones for KITL debug only by setting the following:
   // dpCurSettings.ulZoneMask = 0xFFFF;
   //
   // For somewhat less verbose debugging output, only enable ZONE_ERROR,
   // and ZONE_WARN messages.
   //
   // Also note that ZONE_KITL_ETHER should not be enabled unless you
   // suspect that there may be a problem with CS8900 Ethernet KITL driver.
   // Otherwise, you will get an almost continuous stream of Ethernet frame
   // handling messages from the CS8900 driver which will result in very
   // slow performance of the target device.
   //
   dpCurSettings.ulZoneMask = ZONE_ERROR | ZONE_WARNING | ZONE_KITL_OAL;
#else
   // For non-Debug builds, only display error and warning messages.
   dpCurSettings.ulZoneMask = ZONE_ERROR | ZONE_WARNING;
#endif

   KITL_RETAILMSG(ZONE_KITL_OAL, ("+OEMKitlStartup\r\n"));

   // Get g_nCountsPerMsec for reference in OALGetTickCount()
   // We can get clockFreq from pBspArgs becuase the initialization
   // OALBspArgsInit() had already been done in OEMInit()
   g_nCountsPerMsec = pBspArgs->clockFreq[DDK_CLOCK_SIGNAL_PER] /
                      ((BSP_SYSTIMER_PRESCALAR + 1) * 1000);

   KITL_RETAILMSG(ZONE_KITL_OAL, ("g_nCountsPerMsec = %d\r\n",
                                  g_nCountsPerMsec));

   // Get uncached virtual addresses for timer used in OALGetTickCount.
   g_pEPIT = (PCSP_EPIT_REG) OALPAtoUA(CSP_BASE_REG_PA_EPIT1);
   if (g_pEPIT == NULL) {
      KITL_RETAILMSG(ZONE_ERROR, ("+OEMKitlStartup: EPIT null pointer!\r\n"));
      rc = FALSE;
      goto cleanUp;
   }

   // Look for bootargs left by the bootloader or left over from an earlier
   // boot.
   //
   pArgs      = (OAL_KITL_ARGS*)OALArgsQuery(OAL_ARGS_QUERY_KITL);
   szDeviceId = (CHAR *)OALArgsQuery(OAL_ARGS_QUERY_DEVID);
   kitlFlags  = (UINT *) OALArgsQuery(BSP_ARGS_QUERY_KITL_FLAGS);   // Always valid

   // If we don't get kitl arguments use default
   if (pArgs == NULL) {
      KITL_RETAILMSG(ZONE_WARNING, ("WARN: Boot arguments not found, use defaults\r\n"));
      memset(&args, 0, sizeof(args));
      if (kitlFlags != NULL) {
         args.flags = *kitlFlags;
      }
      else {
         args.flags = 0;
      }

      pKitlDevice = &g_kitlDevices[OAL_KITL_ETH_INDEX];
      args.flags  |= OAL_KITL_FLAGS_ENABLED | OAL_KITL_FLAGS_VMINI | OAL_KITL_FLAGS_DHCP;
      args.mac[0] = (BSP_ARGS_DEFAULT_MAC_BYTE0 | (BSP_ARGS_DEFAULT_MAC_BYTE1 << 8));
      args.mac[1] = (BSP_ARGS_DEFAULT_MAC_BYTE2 | (BSP_ARGS_DEFAULT_MAC_BYTE3 << 8)) ;
      args.mac[2] = (BSP_ARGS_DEFAULT_MAC_BYTE4 | (BSP_ARGS_DEFAULT_MAC_BYTE5 << 8));
      args.devLoc.LogicalLoc = BSP_BASE_REG_PA_LAN911x_IOBASE;
      args.devLoc.IfcType    = Internal;
      pArgs = &args;
   }

#ifndef USE_DHCP_RENEW
   else if (pArgs->flags & OAL_KITL_FLAGS_DHCP) {
      // Reset IP address to force DHCP request instead of renew which
      // is acknowleged much sooner from the DHCP server
      pArgs->ipAddress = 0;
   }
#endif

   // If there isn't a device id from bootloader then create one.
   //
   if ((pArgs->mac[0] == 0) && (pArgs->mac[1] == 0) && (pArgs->mac[2] == 0)) {
      LAN911xInit((UCHAR*)OALPAtoVA((DWORD)pArgs->devLoc.PhysicalLoc, FALSE), pArgs->devLoc.LogicalLoc, pArgs->mac);
   }
   if ((szDeviceId == NULL) || (szDeviceId[0] == '\0')) {
      OALKitlCreateName(BSP_DEVICE_PREFIX, pArgs->mac, buffer);
      szDeviceId = buffer;
   }

   for (numKitlDevice = 0;
        (numKitlDevice < (sizeof(g_kitlDevices)/sizeof(g_kitlDevices[0]))) &&
        (g_kitlDevices[numKitlDevice].name != NULL);
        numKitlDevice++) {
      if (((UINT32)pArgs->devLoc.PhysicalLoc) == g_kitlDevices[numKitlDevice].id) {
         pKitlDevice = &g_kitlDevices[numKitlDevice];
         break;
      }
   }

   if (pKitlDevice == NULL) {
      pKitlDevice = &g_kitlDevices[OAL_KITL_ETH_INDEX];
   }

   // Check to see if we need to increase the KITL thread priority in order
   // to ensure reliable operation.
   if (g_dwKITLThreadPriority != KITL_THREAD_HIGH_PRIORITY) {
      KITL_RETAILMSG(ZONE_KITL_OAL, ("KITL: Increasing KITL thread priority from %d to %d\r\n",
                                     g_dwKITLThreadPriority,
                                     KITL_THREAD_HIGH_PRIORITY));
      g_dwKITLThreadPriority = KITL_THREAD_HIGH_PRIORITY;
   }

   // Can now enable KITL.
   //
   rc = OALKitlInit(szDeviceId, pArgs, g_kitlDevices);

cleanUp:
   KITL_RETAILMSG(ZONE_KITL_OAL, ("-OEMKitlStartup(rc = %d)\r\n", rc));
   return(rc);
}


//-----------------------------------------------------------------------------
//
// FUNCTION:    OALSerialInit
//
// DESCRIPTION:
//      Initializes the internal UART with the specified communication settings.
//
// PARAMETERS:
//      NONE
//
// RETURNS:
//      If this function succeeds, it returns TRUE, otherwise
//      it returns FALSE.
//
//-----------------------------------------------------------------------------
BOOL OALSerialInit(PSERIAL_INFO pSerInfo)
{
    BOOL rc = FALSE;
    _OALKITLSharedDataStruct *pOALKITLSharedData =
        (_OALKITLSharedDataStruct *)(g_pOemGlobal->pKitlInfo);

    // Verify that OEMInit() has provided us with valid pointers
    // for accessing the IOMUX and PBC control registers.
    if (pOALKITLSharedData           &&
        pOALKITLSharedData->g_pIOMUX)
    {
        rc = (OALConfigSerialUART(pSerInfo) &&
              OALConfigSerialIOMUX(pSerInfo->uartBaseAddr,
                                   pOALKITLSharedData->g_pIOMUX));
    }

    return rc;
}


//------------------------------------------------------------------------------
//
//  Function:  OEMKitlSerialInit
//
//  This function is called by OALKitlStart to initialize Serial KITL interface
//
//  Parameters:
//      None
//
//  Returns:
//      None
//------------------------------------------------------------------------------
void OEMKitlSerialInit (void)
{
    SERIAL_INFO serInfo;

    serInfo.baudRate      = BSP_UART_KITL_SERIAL_BAUD;
    serInfo.uartBaseAddr  = BSP_BASE_REG_PA_SERIALKITL;
    serInfo.dataBits      = UART_UCR2_WS_8BIT;
    serInfo.parity        = UART_UCR2_PROE_EVEN;
    serInfo.stopBits      = UART_UCR2_STPB_1STOP;
    serInfo.bParityEnable = FALSE;
    serInfo.flowControl   = FALSE;

    OALSerialInit(&serInfo);
}


//------------------------------------------------------------------------------
//
//  Function:  OALGetTickCount
//
//  This function is called by some KITL libraries to obtain relative time
//  since device boot. It is mostly used to implement timeout in network
//  protocol.
//
//------------------------------------------------------------------------------
UINT32 OALGetTickCount()
{
    UINT32 ticks;

    // Calculate EPIT ticks since we started the system
    ticks = EPIT_CNT_COUNT_MAX - INREG32(&g_pEPIT->CNT);

    // Return the number of 1ms ticks that have elapsed
    // return ticks / g_oalTimer.countsPerMSec;
    //
    // Note: If we want to link oal_timer_mxarm11.lib to get g_oalTimer,
    // the g_oalTimer here will never get initialization.  That's because
    // OAL and KITL have respective copy of g_oalTimer.
    return ticks / g_nCountsPerMsec;
}


//-----------------------------------------------------------------------------
//
// Function: OEMEthGetSecs
//
// Function returns a free-running seconds count.
//
// Parameters:
//
// Returns:
//      DWORD dwSeconds - the count of seconds
//
//-----------------------------------------------------------------------------
DWORD OEMEthGetSecs(void)
{
    SYSTEMTIME st;

    OEMGetRealTime( &st );
    return ((60UL * (60UL * (24UL * (31UL * st.wMonth + st.wDay) + st.wHour) +
             st.wMinute)) + st.wSecond);
}

//------------------------------------------------------------------------------
//
//  Function:  OALKitlInitRegistry
//
//  This function is called during the initialization process to allow the
//  OAL to denote devices which are being used by the KITL connection
//  and thus shouldn't be touched during the OS initialization process.  The
//  OAL provides this information via the registry.
//
VOID OALKitlInitRegistry()
{
   OAL_KITL_ARGS *pKITLArgs;
   HKEY Key;
   DWORD Status;
   DWORD Disposition;
   WCHAR szNULLString[] = TEXT("");

   KITL_RETAILMSG(ZONE_KITL_OAL, (__FUNCTION__ "+\r\n"));

   // If we're using one of the PCCARD slots for the KITL connection, then we
   // should disable the PCCARD driver (via the "no load" option in the registry)
   // in order to avoid a conflict.
   //

   pKITLArgs = (OAL_KITL_ARGS*)OALArgsQuery(OAL_ARGS_QUERY_KITL);

   if (pKITLArgs) {
      if (pKITLArgs->devLoc.PhysicalLoc == (PVOID)OALPAtoUA(CSP_BASE_MEM_PA_PCMCIA_CF + 0x300)) {
         // We're using the PCCARD controller for the KITL connection...

         //
         // *** Disable PCCARD slot 0 instance ***
         //
         Status = NKRegCreateKeyEx(HKEY_LOCAL_MACHINE, L"Drivers\\BuiltIn\\pcc_mx31", 0, NULL, 0, 0, NULL, &Key, &Disposition);

         if (Status == ERROR_SUCCESS) {
            Disposition = DEVFLAGS_NOLOAD;
            // Set Flags value to indicate no loading of driver for this device
            Status = NKRegSetValueEx(Key, DEVLOAD_FLAGS_VALNAME, 0, DEVLOAD_FLAGS_VALTYPE, (PBYTE)&Disposition, sizeof(Disposition));
         }

         // Close the registry key.
         NKRegCloseKey(Key);

         if (Status != ERROR_SUCCESS) {
            KITL_RETAILMSG(ZONE_KITL_OAL||ZONE_ERROR, (__FUNCTION__ ": failed to set \"no load\" key for PCCARD slot 0.\r\n"));
            goto CleanUp;
         }

         KITL_RETAILMSG(ZONE_KITL_OAL, ("INFO: PCCARD being used for KITL - disabling PCCARD driver...\r\n"));
      }
      else if (pKITLArgs->devLoc.PhysicalLoc == (PVOID)OALPAtoUA(BSP_BASE_REG_PA_LAN911x_IOBASE)) {
         // We're using the Lan9217 for the KITL connection...

         //
         // *** Disable Lan9217 instance ***
         //
         Status = NKRegOpenKeyEx(HKEY_LOCAL_MACHINE, L"Comm\\lan9217", 0, 0, &Key);

         if (Status == ERROR_SUCCESS) {
            // Set Flags value to indicate no loading of driver for this device
            Status = NKRegSetValueEx(Key, TEXT("ImagePath"), 0, REG_SZ, (PBYTE)szNULLString, sizeof(szNULLString));
         }

         // Close the registry key.
         NKRegCloseKey(Key);

         if (Status != ERROR_SUCCESS) {
            KITL_RETAILMSG(ZONE_KITL_OAL||ZONE_ERROR, (__FUNCTION__ ": failed to set \"no load\" key for Lan9217.\r\n"));
            goto CleanUp;
         }

         KITL_RETAILMSG(ZONE_KITL_OAL, ("INFO: Lan9217 being used for KITL - disabling Lan9217 NDIS driver...\r\n"));
      }
   }

CleanUp:

   KITL_RETAILMSG(ZONE_KITL_OAL, (__FUNCTION__ "-\r\n"));

   return;
}

//------------------------------------------------------------------------------
BOOL OALIoCtlVBridge(
    UINT32 code, VOID *pInBuffer, UINT32 inSize, VOID *pOutBuffer,
    UINT32 outSize, UINT32 *pOutSize);

//------------------------------------------------------------------------------
//
//  Function:  OEMKitlIoctl
//
//  This function handles KITL IOCTL codes.
//
//
BOOL OEMKitlIoctl(DWORD code, VOID * pInBuffer, DWORD inSize, VOID * pOutBuffer, DWORD outSize, DWORD * pOutSize)
{
   BOOL fRet = FALSE;

   switch (code) {
      case IOCTL_HAL_INITREGISTRY:
         OALKitlInitRegistry();
         // Return FALSE & ERROR_NOT_SUPPORTED to yield to OEMIoCtrl
         NKSetLastError(ERROR_NOT_SUPPORTED);
         break;

      default:
         fRet = OALIoCtlVBridge(code, pInBuffer, inSize, pOutBuffer, outSize, pOutSize);
         break;
   }

   return fRet;
}


