/** @file
*
*  Copyright (c) 2012, ARM Limited. All rights reserved.
*  
*  This program and the accompanying materials                          
*  are licensed and made available under the terms and conditions of the BSD License         
*  which accompanies this distribution.  The full text of the license may be found at        
*  http://opensource.org/licenses/bsd-license.php                                            
*
*  THE PROGRAM IS DISTRIBUTED UNDER THE BSD LICENSE ON AN "AS IS" BASIS,                     
*  WITHOUT WARRANTIES OR REPRESENTATIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED.             
*
**/

#include <Base.h>
#include <Library/ArmLib.h>
#include <Library/ArmCpuLib.h>
#include <Library/ArmV7ArchTimerLib.h>
#include <Library/BakeryLockLib.h>
#include <Library/DebugLib.h>
#include <Library/IoLib.h>
#include <Library/PcdLib.h>

#include <Chipset/ArmCortexA15.h>
#include <Drivers/ArmCacheCoherentInterconnect.h>

VOID
ArmCpuSetup (
  IN  UINTN         MpId
  )
{
  UINTN     CoreId;
  UINT32    IdCode;
  UINTN     CciSlavePort;

  // The Primary Core initializes the CCI SpinLock
  if (IS_PRIMARY_CORE(MpId)) {
    BakeryLockInit (PcdGet32 (PcdCacheCoherentInterconnectSpinLockOffset));
  }

  // Check if Architectural Timer frequency is valid number (should not be 0)
  ASSERT (PcdGet32 (PcdArmArchTimerFreqInHz));
  ASSERT(ArmIsArchTimerImplemented () != 0);

  // Enable SWP instructions
  ArmEnableSWPInstruction ();

  // Enable program flow prediction, if supported.
  ArmEnableBranchPrediction ();

  // Note: System Counter frequency can only be set in Secure privileged mode,
  // if security extensions are implemented.
  ArmArchTimerSetTimerFreq (PcdGet32 (PcdArmArchTimerFreqInHz));

  // Turn on SMP coherency
  ArmSetAuxCrBit (A15_FEATURE_SMP);

  // Get the associated CCI Slave Port
  IdCode = Cp15IdCode();
  if ((IdCode & (UINT32)(ARM_CPU_IMPLEMENTER_MASK | ARM_CPU_PRIMARY_PART_MASK)) == (ARM_CPU_IMPLEMENTER_ARMLTD | ARM_CPU_PRIMARY_PART_CORTEXA15)) {
    CciSlavePort = PcdGet32 (PcdCacheCoherentInterconnectA15AceSlavePort);
  } else if ((IdCode & (UINT32)(ARM_CPU_IMPLEMENTER_MASK | ARM_CPU_PRIMARY_PART_MASK)) == (ARM_CPU_IMPLEMENTER_ARMLTD | ARM_CPU_PRIMARY_PART_CORTEXA7)) {
    CciSlavePort = PcdGet32 (PcdCacheCoherentInterconnectA7AceSlavePort);
  } else {
    CciSlavePort = 0;
    ASSERT (0);
  }

  // Acquire lock
  CoreId = (GET_CLUSTER_ID(MpId) * (PcdGet32 (PcdBakeryLockMaxCpus)/2)) + GET_CORE_ID(MpId);
  BakeryLockGet (PcdGet32 (PcdCacheCoherentInterconnectSpinLockOffset), CoreId);

  // Enabled CCI if not enabled for the Slave Port Interface
  if (CciSlavePort && !ArmCacheCoherentInterconnectIsEnabled (CciSlavePort)) {
    ArmCacheCoherentInterconnectEnable (CciSlavePort);
  }

  // Release lock
  BakeryLockRelease (PcdGet32 (PcdCacheCoherentInterconnectSpinLockOffset), CoreId);
}

VOID
ArmCpuSetupSmpNonSecure (
  IN  UINTN         MpId
  )
{
}

UINTN
ArmCpuSecureContextGetSize (
  VOID
  )
{
  return sizeof (ARM_CPU_SECURE_CONTEXT);
}

VOID
ArmCpuSecureContextSave (
  IN UINTN              CpuContextBase
  )
{
  ARM_CPU_SECURE_CONTEXT        *Context;

  // Check the context base address has been defined
  ASSERT (CpuContextBase != 0);

  Context = (ARM_CPU_SECURE_CONTEXT*)CpuContextBase;

  Context->MVBar = ArmReadMVBar ();
  Context->VBar = ArmReadVBar ();
  Context->NSacr = ArmReadNsacr ();
  Context->CPacr = ArmReadCpacr ();
  Context->Scr = ArmReadScr ();
  Context->Sctlr = ArmReadSctlr ();
  Context->CntFrq = ArmArchTimerGetTimerFreq ();
}

VOID
ArmCpuSecureContextRestore (
  IN UINTN              CpuContextBase
  )
{
  ARM_CPU_SECURE_CONTEXT        *Context;

  // Check the context base address has been defined
  ASSERT (CpuContextBase != 0);

  Context = (ARM_CPU_SECURE_CONTEXT*)CpuContextBase;

  ArmWriteMVBar (Context->MVBar);
  ArmWriteVBar (Context->VBar);
  ArmWriteCpacr (Context->CPacr);
  ArmArchTimerSetTimerFreq (Context->CntFrq);

  ArmWriteNsacr (Context->NSacr);
  ArmWriteScr (Context->Scr);
}
