/** @file

  Copyright (c) 2020 - 2021, NUVIA Inc. All rights reserved.<BR>

  SPDX-License-Identifier: BSD-2-Clause-Patent

**/

#ifndef ARM_CACHE_H_
#define ARM_CACHE_H_

#include <Uefi/UefiBaseType.h>

// The ARM Architecture Reference Manual for ARMv8-A defines up
// to 7 levels of cache, L1 through L7.
#define MAX_ARM_CACHE_LEVEL  7

/// Defines the structure of the CSSELR (Cache Size Selection) register
typedef union {
  struct {
    UINT32    InD      : 1;  ///< Instruction not Data bit
    UINT32    Level    : 3;  ///< Cache level (zero based)
    UINT32    TnD      : 1;  ///< Allocation not Data bit
    UINT32    Reserved : 27; ///< Reserved, RES0
  } Bits;         ///< Bitfield definition of the register
  UINT32    Data; ///< The entire 32-bit value
} CSSELR_DATA;

/// The cache type values for the InD field of the CSSELR register
typedef enum {
  /// Select the data or unified cache
  CsselrCacheTypeDataOrUnified = 0,
  /// Select the instruction cache
  CsselrCacheTypeInstruction,
  CsselrCacheTypeMax
} CSSELR_CACHE_TYPE;

/// Defines the structure of the CCSIDR (Current Cache Size ID) register
typedef union {
  struct {
    UINT64    LineSize      : 3;      ///< Line size (Log2(Num bytes in cache) - 4)
    UINT64    Associativity : 10;     ///< Associativity - 1
    UINT64    NumSets       : 15;     ///< Number of sets in the cache -1
    UINT64    Unknown       : 4;      ///< Reserved, UNKNOWN
    UINT64    Reserved      : 32;     ///< Reserved, RES0
  } BitsNonCcidx; ///< Bitfield definition of the register when FEAT_CCIDX is not supported.
  struct {
    UINT64    LineSize      : 3;      ///< Line size (Log2(Num bytes in cache) - 4)
    UINT64    Associativity : 21;     ///< Associativity - 1
    UINT64    Reserved1     : 8;      ///< Reserved, RES0
    UINT64    NumSets       : 24;     ///< Number of sets in the cache -1
    UINT64    Reserved2     : 8;      ///< Reserved, RES0
  } BitsCcidxAA64; ///< Bitfield definition of the register when FEAT_IDX is supported.
  struct {
    UINT64    LineSize      : 3;
    UINT64    Associativity : 21;
    UINT64    Reserved      : 8;
    UINT64    Unallocated   : 32;
  } BitsCcidxAA32;
  UINT64    Data; ///< The entire 64-bit value
} CCSIDR_DATA;

/// Defines the structure of the AARCH32 CCSIDR2 register.
typedef union {
  struct {
    UINT32    NumSets  : 24;          ///< Number of sets in the cache - 1
    UINT32    Reserved : 8;           ///< Reserved, RES0
  } Bits;         ///< Bitfield definition of the register
  UINT32    Data; ///< The entire 32-bit value
} CCSIDR2_DATA;

/** Defines the structure of the CLIDR (Cache Level ID) register.
 *
 * The lower 32 bits are the same for both AARCH32 and AARCH64
 * so we can use the same structure for both.
**/
typedef union {
  struct {
    UINT32    Ctype1 : 3;   ///< Level 1 cache type
    UINT32    Ctype2 : 3;   ///< Level 2 cache type
    UINT32    Ctype3 : 3;   ///< Level 3 cache type
    UINT32    Ctype4 : 3;   ///< Level 4 cache type
    UINT32    Ctype5 : 3;   ///< Level 5 cache type
    UINT32    Ctype6 : 3;   ///< Level 6 cache type
    UINT32    Ctype7 : 3;   ///< Level 7 cache type
    UINT32    LoUIS  : 3;   ///< Level of Unification Inner Shareable
    UINT32    LoC    : 3;   ///< Level of Coherency
    UINT32    LoUU   : 3;   ///< Level of Unification Uniprocessor
    UINT32    Icb    : 3;   ///< Inner Cache Boundary
  } Bits;         ///< Bitfield definition of the register
  UINT32    Data; ///< The entire 32-bit value
} CLIDR_DATA;

/// The cache types reported in the CLIDR register.
typedef enum {
  /// No cache is present
  ClidrCacheTypeNone = 0,
  /// There is only an instruction cache
  ClidrCacheTypeInstructionOnly,
  /// There is only a data cache
  ClidrCacheTypeDataOnly,
  /// There are separate data and instruction caches
  ClidrCacheTypeSeparate,
  /// There is a unified cache
  ClidrCacheTypeUnified,
  ClidrCacheTypeMax
} CLIDR_CACHE_TYPE;

#define CLIDR_GET_CACHE_TYPE(x, level)  ((x >> (3 * (level))) & 0b111)

#endif /* ARM_CACHE_H_ */
