/** @file
*
*  Copyright (c) 2011, 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 <Library/IoLib.h>
#include <Library/DebugLib.h>
#include <Library/ArmLib.h>
#include <Drivers/PL310L2Cache.h>
#include <Library/PcdLib.h>

#define L2x0WriteReg(reg,val)               MmioWrite32(PcdGet32(PcdL2x0ControllerBase) + reg, val)
#define L2x0ReadReg(reg)                    MmioRead32(PcdGet32(PcdL2x0ControllerBase) + reg)

// Initialize PL320 L2 Cache Controller
VOID
L2x0CacheInit (
  IN  UINTN   L2x0Base,
  IN  UINT32  L2x0TagLatencies,
  IN  UINT32  L2x0DataLatencies,
  IN  UINT32  L2x0AuxValue,
  IN  UINT32  L2x0AuxMask,
  IN  BOOLEAN CacheEnabled
  )
{
  UINT32 Data;
  UINT32 Revision;
  UINT32 Aux;
  UINT32 PfCtl;
  UINT32 PwrCtl;

  // Check if L2x0 is present and is an ARM implementation
  Data = L2x0ReadReg(L2X0_CACHEID);
  if ((Data >> 24) != L2X0_CACHEID_IMPLEMENTER_ARM) {
    ASSERT(0);
    return;
  }

  // Check if L2x0 is PL310
  if (((Data >> 6) & 0xF) != L2X0_CACHEID_PARTNUM_PL310) {
    ASSERT(0);
    return;
  }

  // RTL release
  Revision = Data & 0x3F;

  // Check if L2x0 is already enabled then we disable it
  Data = L2x0ReadReg(L2X0_CTRL);
  if (Data & L2X0_CTRL_ENABLED) {
    L2x0WriteReg(L2X0_CTRL, L2X0_CTRL_DISABLED);
  }

  //
  // Set up global configurations
  //

  // Auxiliary register: Non-secure interrupt access Control + Event monitor bus enable + SBO
  Aux = L2X0_AUXCTRL_NSAC | L2X0_AUXCTRL_EM | L2X0_AUXCTRL_SBO;
  // Use AWCACHE attributes for WA
  Aux |= L2x0_AUXCTRL_AW_AWCACHE;
  // Use default Size
  Data = L2x0ReadReg(L2X0_AUXCTRL);
  Aux |= Data & L2X0_AUXCTRL_WAYSIZE_MASK;
  // Use default associativity
  Aux |= Data & L2X0_AUXCTRL_ASSOCIATIVITY;
  // Enabled I & D Prefetch
  Aux |= L2x0_AUXCTRL_IPREFETCH | L2x0_AUXCTRL_DPREFETCH;

  if (Revision >= 5) {
    // Prefetch Offset Register
    PfCtl = L2x0ReadReg(L2X0_PFCTRL);
    // - Prefetch increment set to 0
    // - Prefetch dropping off
    // - Double linefills off
    L2x0WriteReg(L2X0_PFCTRL, PfCtl);

    // Power Control Register - L2X0_PWRCTRL
    PwrCtl = L2x0ReadReg(L2X0_PWRCTRL);
    // - Standby when idle off
    // - Dynamic clock gating off
    // - Nc,NC-shared dropping off
    L2x0WriteReg(L2X0_PWRCTRL, PwrCtl);
  }

  if (Revision >= 2) {
    L2x0WriteReg(L230_TAG_LATENCY, L2x0TagLatencies);
    L2x0WriteReg(L230_DATA_LATENCY, L2x0DataLatencies);
  } else {
    // PL310 old style latency is not supported yet
    ASSERT(0);
  }

  // Set the platform specific values
  Aux = (Aux & L2x0AuxMask) | L2x0AuxValue;

  // Write Auxiliary value
  L2x0WriteReg(L2X0_AUXCTRL, Aux);

  //
  // Invalidate all entries in cache
  //
  L2x0WriteReg(L2X0_INVWAY, 0xffff);
  // Poll cache maintenance register until invalidate operation is complete
  while(L2x0ReadReg(L2X0_INVWAY) & 0xffff);

  // Write to the Lockdown D and Lockdown I Register 9 if required
  // - Not required

  // Clear any residual raw interrupts
  L2x0WriteReg(L2X0_INTCLEAR, 0x1FF);

  // Enable the cache
  if (CacheEnabled) {
    L2x0WriteReg(L2X0_CTRL, L2X0_CTRL_ENABLED);
  }
}
