//
// 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
//
//------------------------------------------------------------------------------

#include <bsp.h>

extern PCSP_CCM_REGS g_pCCM;
//extern PCSP_PBC_REGS g_pPBC;

//------------------------------------------------------------------------------
// Local Variables


//------------------------------------------------------------------------------
// Functions


//------------------------------------------------------------------------------
//
//  Function:   OALBspArgsInit
//
//  This function reads the user switch setting on the ADS board and configures
//  parameters of the global BSP args structure.
//
//  Parameters:
//      pBSPArgs
//          [out] Points to BSP arguments structure to be updated.
//
//  Returns:
//      TRUE if boot successfully, otherwise returns FALSE.
//
//------------------------------------------------------------------------------
BOOL OALBspArgsInit(BSP_ARGS *pBSPArgs)
{
   UINT16 dsw;
   UINT32 pdr, ccmr, div, pllRef, mcuPllFreq, serPllFreq, usbPllFreq, ahbFreq;
   UINT32 pctl, mfi, mfn, mfd, pdf;

   // Read board user switch setting
   dsw = INREG16(&pBSPArgs->BSTAT2) & 0xFF;
#ifdef DEBUG
   OALMSG(OAL_INFO, (TEXT("PBC debug switch settings:  0x%x\r\n"), dsw));
#endif //#ifdef DEBUG
   // Initialize args structure with KITL configuration
   if (dsw & BSP_PBC_DSW_KITL) {
      pBSPArgs->kitl.flags |= OAL_KITL_FLAGS_PASSIVE;
      OALMSG(OAL_INFO, (TEXT("KITL will run in PASSIVE mode\r\n")));
   }

   // Initialize args structure with L2 configuration
   pBSPArgs->bL2enable = !(dsw & BSP_PBC_DSW_L2);

   // Determine if we will switch to high speed after PMIC supply
   // voltages have been configured.
   pBSPArgs->bHighSpeedEnable = !(dsw & BSP_PBC_DSW_ARM_CLK);

   ccmr = INREG32(&g_pCCM->CCMR);

   // Check for CKIH PLL reference
   if (CSP_BITFEXT(ccmr, CCM_CCMR_PRCS) == CCM_CCMR_PRCS_CKIH_REF) {
      // Check for alternate CKIH (i.e. supplied from TV encoder card)
      if (dsw & BSP_PBC_DSW_ALT_CLK) {
         pllRef = BSP_CLK_CKIH_FREQ;
      }
      else {
         // Else, use on-bard CKIH
         pllRef = BSP_CLK_CKIH_FREQ;
      }
   }
   else {
      // PLL referece is FPM output
      pllRef = BSP_CLK_FPM_FREQ;
   }

   pctl = INREG32(&g_pCCM->MPCTL);
   mfi = CSP_BITFEXT(pctl, CCM_MPCTL_MFI);
   mfn = CSP_BITFEXT(pctl, CCM_MPCTL_MFN);
   mfd = CSP_BITFEXT(pctl, CCM_MPCTL_MFD);
   pdf = CSP_BITFEXT(pctl, CCM_MPCTL_PDF);

   mcuPllFreq = (UINT32) (((2 * pllRef * mfi) + ((UINT64) 2 * pllRef * mfn)/(mfd+1)) / (pdf+1));

   pctl = INREG32(&g_pCCM->SRPCTL);
   mfi = CSP_BITFEXT(pctl, CCM_SPCTL_MFI);
   mfn = CSP_BITFEXT(pctl, CCM_SPCTL_MFN);
   mfd = CSP_BITFEXT(pctl, CCM_SPCTL_MFD);
   pdf = CSP_BITFEXT(pctl, CCM_SPCTL_PDF);

   serPllFreq = (UINT32) (((2 * pllRef * mfi) + ((UINT64) 2 * pllRef * mfn)/(mfd+1)) / (pdf+1));
   pctl = INREG32(&g_pCCM->UPCTL);
   mfi = CSP_BITFEXT(pctl, CCM_UPCTL_MFI);
   mfn = CSP_BITFEXT(pctl, CCM_UPCTL_MFN);
   mfd = CSP_BITFEXT(pctl, CCM_UPCTL_MFD);
   pdf = CSP_BITFEXT(pctl, CCM_UPCTL_PDF);
#ifdef DEBUG
   OALMSG(OAL_INFO, (_T("mfi = %d, mfn = %d, mfd = %d, pdf = %d\r\n"), mfi, mfn, mfd, pdf));
#endif //#ifdef DEBUG
   usbPllFreq = (UINT32) (((2 * pllRef * mfi) + ((UINT64) 2 * pllRef * mfn)/(mfd+1)) / (pdf+1));

   // Initialize PLL clock configuration
   pBSPArgs->clockFreq[DDK_CLOCK_SIGNAL_MCUPLL] = mcuPllFreq;
   pBSPArgs->clockFreq[DDK_CLOCK_SIGNAL_SERPLL] = serPllFreq;
   pBSPArgs->clockFreq[DDK_CLOCK_SIGNAL_USBPLL] = usbPllFreq;

   // Get post dividers for MCU clock domain
   pdr = INREG32(&g_pCCM->PDR0);

   // Calculate MCU clock frequency
   div = CSP_BITFEXT(pdr, CCM_PDR0_MCU_PODF);
   pBSPArgs->clockFreq[DDK_CLOCK_SIGNAL_ARM] = mcuPllFreq / (div + 1);

   // Calculate HSP (IPU) clock frequency
   div = CSP_BITFEXT(pdr, CCM_PDR0_HSP_PODF);
   pBSPArgs->clockFreq[DDK_CLOCK_SIGNAL_IPU] = mcuPllFreq / (div + 1);

   // Calculate AHB (MAX) clock frequency
   div = CSP_BITFEXT(pdr, CCM_PDR0_MAX_PODF);
   ahbFreq = mcuPllFreq / (div + 1);
   pBSPArgs->clockFreq[DDK_CLOCK_SIGNAL_AHB] = ahbFreq;

   // Calculate IPG clock frequency
   div = CSP_BITFEXT(pdr, CCM_PDR0_IPG_PODF);
   pBSPArgs->clockFreq[DDK_CLOCK_SIGNAL_IPG] = ahbFreq / (div + 1);

   // Calculate NFC clock frequency
   div = CSP_BITFEXT(pdr, CCM_PDR0_NFC_PODF);
   pBSPArgs->clockFreq[DDK_CLOCK_SIGNAL_NFC] = ahbFreq / (div + 1);

   // Calculate GACC clock frequency
   pBSPArgs->clockFreq[DDK_CLOCK_SIGNAL_GACC] = ahbFreq >> 1;

   // Calculate PER clock frequency
   div = CSP_BITFEXT(pdr, CCM_PDR0_PER_PODF);

   // PER clock source can be IPG_CLK or USBPLL and is selected by PERCS
   if (CSP_BITFEXT(ccmr, CCM_CCMR_PERCS) == CCM_CCMR_PERCS_USB_CLK) {
      pBSPArgs->clockFreq[DDK_CLOCK_SIGNAL_PER] = usbPllFreq / (div + 1);
   }
   else {
      pBSPArgs->clockFreq[DDK_CLOCK_SIGNAL_PER] = pBSPArgs->clockFreq[DDK_CLOCK_SIGNAL_IPG];
   }

   // Calculate SIM clock frequency (same as PERCLK)
   pBSPArgs->clockFreq[DDK_CLOCK_SIGNAL_SIM] = pBSPArgs->clockFreq[DDK_CLOCK_SIGNAL_PER];

   // Calculate CSI clock frequency
   div = CSP_BITFEXT(pdr, CCM_PDR0_CSI_PODF) + 1;
   switch (CSP_BITFEXT(ccmr, CCM_CCMR_CSCS)) {
      case CCM_CCMR_CSCS_USB_CLK:
         pBSPArgs->clockFreq[DDK_CLOCK_SIGNAL_CSI] = usbPllFreq / div;
         break;

      case CCM_CCMR_CSCS_SERIAL_CLK:
         pBSPArgs->clockFreq[DDK_CLOCK_SIGNAL_CSI] = serPllFreq / div;
         break;
   }

   // Get post dividers for peripheral baud clocks
   pdr = INREG32(&g_pCCM->PDR1);

   // Calculate SSI1 clock frequency
   div = CSP_BITFEXT(pdr, CCM_PDR1_SSI1_PRE_PODF) + 1;
   div *= (CSP_BITFEXT(pdr, CCM_PDR1_SSI1_PODF) + 1);
   switch (CSP_BITFEXT(ccmr, CCM_CCMR_SSI1S)) {
      case CCM_CCMR_SSI1S_MCU_CLK:
         pBSPArgs->clockFreq[DDK_CLOCK_SIGNAL_SSI1] = mcuPllFreq / div;
         break;

      case CCM_CCMR_SSI1S_USB_CLK:
         pBSPArgs->clockFreq[DDK_CLOCK_SIGNAL_SSI1] = usbPllFreq / div;
         break;

      case CCM_CCMR_SSI1S_SERIAL_CLK:
         pBSPArgs->clockFreq[DDK_CLOCK_SIGNAL_SSI1] = serPllFreq / div;
         break;
   }

   // Calculate SSI2 clock frequency
   div = CSP_BITFEXT(pdr, CCM_PDR1_SSI2_PRE_PODF) + 1;
   div *= (CSP_BITFEXT(pdr, CCM_PDR1_SSI2_PODF) + 1);
   switch (CSP_BITFEXT(ccmr, CCM_CCMR_SSI2S)) {
      case CCM_CCMR_SSI2S_MCU_CLK:
         pBSPArgs->clockFreq[DDK_CLOCK_SIGNAL_SSI2] = mcuPllFreq / div;
         break;

      case CCM_CCMR_SSI2S_USB_CLK:
         pBSPArgs->clockFreq[DDK_CLOCK_SIGNAL_SSI2] = usbPllFreq / div;
         break;

      case CCM_CCMR_SSI2S_SERIAL_CLK:
         pBSPArgs->clockFreq[DDK_CLOCK_SIGNAL_SSI2] = serPllFreq / div;
         break;
   }

   // Calculate FIRI clock frequency
   div = CSP_BITFEXT(pdr, CCM_PDR1_FIRI_PRE_PODF) + 1;
   div *= (CSP_BITFEXT(pdr, CCM_PDR1_FIRI_PODF) + 1);
   switch (CSP_BITFEXT(ccmr, CCM_CCMR_FIRS)) {
      case CCM_CCMR_FIRS_MCU_CLK:
         pBSPArgs->clockFreq[DDK_CLOCK_SIGNAL_FIRI] = mcuPllFreq / div;
         break;

      case CCM_CCMR_FIRS_USB_CLK:
         pBSPArgs->clockFreq[DDK_CLOCK_SIGNAL_FIRI] = usbPllFreq / div;
         break;

      case CCM_CCMR_FIRS_SERIAL_CLK:
         pBSPArgs->clockFreq[DDK_CLOCK_SIGNAL_FIRI] = serPllFreq / div;
         break;
   }

	OALMSG(OAL_INFO, (L"\r\n"));
	OALMSG(OAL_INFO, (L" ----------------------------------------- \r\n"));
	OALMSG(OAL_INFO, (L"  iMX31 Clock Information \r\n"));
	OALMSG(OAL_INFO, (L"  ....................................... \r\n"));

   // Calculate USB clock frequency
   div = CSP_BITFEXT(pdr, CCM_PDR1_USB_PRDF) + 1;
   div *= (CSP_BITFEXT(pdr, CCM_PDR1_USB_PODF) + 1);
   pBSPArgs->clockFreq[DDK_CLOCK_SIGNAL_USB] = usbPllFreq / div;
   OALMSG(OAL_INFO, (TEXT("    MCU PLL  = %d Hz\r\n"),
          pBSPArgs->clockFreq[DDK_CLOCK_SIGNAL_MCUPLL]));
   OALMSG(OAL_INFO, (TEXT("    USB PLL  = %d Hz\r\n"),
          pBSPArgs->clockFreq[DDK_CLOCK_SIGNAL_USBPLL]));
   OALMSG(OAL_INFO, (TEXT("    SER PLL  = %d Hz\r\n"),
          pBSPArgs->clockFreq[DDK_CLOCK_SIGNAL_SERPLL]));
   OALMSG(OAL_INFO, (TEXT("    ARM CLOCK  = %d Hz\r\n"),
          pBSPArgs->clockFreq[DDK_CLOCK_SIGNAL_ARM]));
   OALMSG(OAL_INFO, (TEXT("    IPU CLOCK  = %d Hz\r\n"),
          pBSPArgs->clockFreq[DDK_CLOCK_SIGNAL_IPU]));
   OALMSG(OAL_INFO, (TEXT("    AHB CLOCK  = %d Hz\r\n"),
          pBSPArgs->clockFreq[DDK_CLOCK_SIGNAL_AHB]));
#ifdef DEBUG
   OALMSG(OAL_INFO, (TEXT("    IPG CLOCK  = %d Hz\r\n"),
          pBSPArgs->clockFreq[DDK_CLOCK_SIGNAL_IPG]));
   OALMSG(OAL_INFO, (TEXT("    NFC CLOCK  = %d Hz\r\n"),
          pBSPArgs->clockFreq[DDK_CLOCK_SIGNAL_NFC]));
   OALMSG(OAL_INFO, (TEXT("    PER CLOCK = %d Hz\r\n"),
          pBSPArgs->clockFreq[DDK_CLOCK_SIGNAL_PER]));
   OALMSG(OAL_INFO, (TEXT("    SSI1 CLOCK = %d Hz\r\n"),
          pBSPArgs->clockFreq[DDK_CLOCK_SIGNAL_SSI1]));
   OALMSG(OAL_INFO, (TEXT("    SSI2 CLOCK = %d Hz\r\n"),
          pBSPArgs->clockFreq[DDK_CLOCK_SIGNAL_SSI2]));
   OALMSG(OAL_INFO, (TEXT("    FIRI CLOCK = %d Hz\r\n"),
          pBSPArgs->clockFreq[DDK_CLOCK_SIGNAL_FIRI]));
   OALMSG(OAL_INFO, (TEXT("    CSI CLOCK = %d Hz\r\n"),
          pBSPArgs->clockFreq[DDK_CLOCK_SIGNAL_CSI]));
   OALMSG(OAL_INFO, (TEXT("    USB CLOCK = %d Hz\r\n"),
          pBSPArgs->clockFreq[DDK_CLOCK_SIGNAL_USB]));
   OALMSG(OAL_INFO, (TEXT("    SIM CLOCK = %d Hz\r\n"),
          pBSPArgs->clockFreq[DDK_CLOCK_SIGNAL_SIM]));
#else
	OALMSG(OAL_INFO, (L"   More Information Available in Debug Build \r\n"));

#endif //#ifdef DEBUG
	OALMSG(1, (L" ----------------------------------------- \r\n"));
   return TRUE;
}


//------------------------------------------------------------------------------
