/**
 * @file
 *
 * @brief Information Required to Build RTEMS for a Particular Member
 * of the SPARC Family
 *
 * This file contains the information required to build
 * RTEMS for a particular member of the SPARC family.  It does
 * this by setting variables to indicate which implementation
 * dependent features are present in a particular member
 * of the family.
 */

/*
 *  COPYRIGHT (c) 1989-2011.
 *  On-Line Applications Research Corporation (OAR).
 *
 *  The license and distribution terms for this file may be
 *  found in the file LICENSE in this distribution or at
 *  http://www.rtems.org/license/LICENSE.
 */

#ifndef _RTEMS_SCORE_SPARC_H
#define _RTEMS_SCORE_SPARC_H

#include <rtems/score/basedefs.h>

#ifdef __cplusplus
extern "C" {
#endif

/*
 *
 *  Currently recognized feature flags:
 *
 *    + SPARC_HAS_FPU
 *        0 - no HW FPU
 *        1 - has HW FPU (assumed to be compatible w/90C602)
 *
 *    + SPARC_HAS_BITSCAN
 *        0 - does not have scan instructions
 *        1 - has scan instruction  (not currently implemented)
 *
 *    + SPARC_NUMBER_OF_REGISTER_WINDOWS
 *        8 is the most common number supported by SPARC implementations.
 *        SPARC_PSR_CWP_MASK is derived from this value.
 */

/**
 * Some higher end SPARCs have a bitscan instructions. It would
 * be nice to take advantage of them.  Right now, there is no
 * port to a CPU model with this feature and no (untested) code
 * that is based on this feature flag.
 */
#define SPARC_HAS_BITSCAN                0

/**
 * This should be OK until a port to a higher end SPARC processor
 * is made that has more than 8 register windows.  If this cannot
 * be determined based on multilib settings (v7/v8/v9), then the
 * cpu_asm.S code that depends on this will have to move to libcpu.
 */
#define SPARC_NUMBER_OF_REGISTER_WINDOWS 8

/**
 * See GRLIB-TN-0009: "LEON3FT Stale Cache Entry After Store with
 * Data Tag Parity Error"
 */
#if defined(__FIX_LEON3FT_B2BST)
  #define SPARC_LEON3FT_B2BST_NOP nop
#else
  #define SPARC_LEON3FT_B2BST_NOP
#endif

/**
 * This macro indicates whether this multilib variation has hardware
 * floating point or not.  We use the gcc cpp predefine _SOFT_FLOAT
 * to determine that.
 */
#if defined(_SOFT_FLOAT)
  #define SPARC_HAS_FPU 0
#else
  #define SPARC_HAS_FPU 1
#endif

/**
 * This macro contains a string describing the multilib variant being
 * build.
 */
#if SPARC_HAS_FPU
  #define CPU_MODEL_NAME "w/FPU"
#else
  #define CPU_MODEL_NAME "w/soft-float"
#endif

/**
 * Define the name of the CPU family.
 */
#define CPU_NAME "SPARC"

/*
 *  Miscellaneous constants
 */

/**
 * PSR masks and starting bit positions
 *
 * NOTE: Reserved bits are ignored.
 */
#if (SPARC_NUMBER_OF_REGISTER_WINDOWS == 8)
  #define SPARC_PSR_CWP_MASK               0x07   /* bits  0 -  4 */
#elif (SPARC_NUMBER_OF_REGISTER_WINDOWS == 16)
  #define SPARC_PSR_CWP_MASK               0x0F   /* bits  0 -  4 */
#elif (SPARC_NUMBER_OF_REGISTER_WINDOWS == 32)
  #define SPARC_PSR_CWP_MASK               0x1F   /* bits  0 -  4 */
#else
  #error "Unsupported number of register windows for this cpu"
#endif

/** This constant is a mask for the ET bits in the PSR. */
#define SPARC_PSR_ET_MASK   0x00000020   /* bit   5 */
/** This constant is a mask for the PS bits in the PSR. */
#define SPARC_PSR_PS_MASK   0x00000040   /* bit   6 */
/** This constant is a mask for the S bits in the PSR. */
#define SPARC_PSR_S_MASK    0x00000080   /* bit   7 */
/** This constant is a mask for the PIL bits in the PSR. */
#define SPARC_PSR_PIL_MASK  0x00000F00   /* bits  8 - 11 */
/** This constant is a mask for the EF bits in the PSR. */
#define SPARC_PSR_EF_MASK   0x00001000   /* bit  12 */
/** This constant is a mask for the EC bits in the PSR. */
#define SPARC_PSR_EC_MASK   0x00002000   /* bit  13 */
/** This constant is a mask for the ICC bits in the PSR. */
#define SPARC_PSR_ICC_MASK  0x00F00000   /* bits 20 - 23 */
/** This constant is a mask for the VER bits in the PSR. */
#define SPARC_PSR_VER_MASK  0x0F000000   /* bits 24 - 27 */
/** This constant is a mask for the IMPL bits in the PSR. */
#define SPARC_PSR_IMPL_MASK 0xF0000000   /* bits 28 - 31 */

/** This constant is the starting bit position of the CWP in the PSR. */
#define SPARC_PSR_CWP_BIT_POSITION   0   /* bits  0 -  4 */
/** This constant is the starting bit position of the ET in the PSR. */
#define SPARC_PSR_ET_BIT_POSITION    5   /* bit   5 */
/** This constant is the starting bit position of the PS in the PSR. */
#define SPARC_PSR_PS_BIT_POSITION    6   /* bit   6 */
/** This constant is the starting bit position of the S in the PSR. */
#define SPARC_PSR_S_BIT_POSITION     7   /* bit   7 */
/** This constant is the starting bit position of the PIL in the PSR. */
#define SPARC_PSR_PIL_BIT_POSITION   8   /* bits  8 - 11 */
/** This constant is the starting bit position of the EF in the PSR. */
#define SPARC_PSR_EF_BIT_POSITION   12   /* bit  12 */
/** This constant is the starting bit position of the EC in the PSR. */
#define SPARC_PSR_EC_BIT_POSITION   13   /* bit  13 */
/** This constant is the starting bit position of the ICC in the PSR. */
#define SPARC_PSR_ICC_BIT_POSITION  20   /* bits 20 - 23 */
/** This constant is the starting bit position of the VER in the PSR. */
#define SPARC_PSR_VER_BIT_POSITION  24   /* bits 24 - 27 */
/** This constant is the starting bit position of the IMPL in the PSR. */
#define SPARC_PSR_IMPL_BIT_POSITION 28   /* bits 28 - 31 */

#define LEON3_ASR17_PROCESSOR_INDEX_SHIFT 28

/* SPARC Software Trap number definitions */
#define SPARC_SWTRAP_SYSCALL 0
#define SPARC_SWTRAP_IRQDIS 9
#define SPARC_SWTRAP_IRQEN 10
#if SPARC_HAS_FPU == 1
#define SPARC_SWTRAP_IRQDIS_FP 11
#endif

#ifndef ASM

/**
 * This macro is a standard nop instruction.
 */
#define nop() \
  do { \
    __asm__ volatile ( "nop" ); \
  } while ( 0 )

/**
 * @brief Macro to obtain the PSR.
 *
 * This macro returns the current contents of the PSR register in @a _psr.
 */
#if defined(RTEMS_PARAVIRT)

uint32_t _SPARC_Get_PSR( void );

#define sparc_get_psr( _psr ) \
  (_psr) = _SPARC_Get_PSR()

#else /* RTEMS_PARAVIRT */

#define sparc_get_psr( _psr ) \
  do { \
     (_psr) = 0; \
     __asm__ volatile( "rd %%psr, %0" :  "=r" (_psr) : "0" (_psr) ); \
  } while ( 0 )

#endif /* RTEMS_PARAVIRT */

/**
 * @brief Macro to set the PSR.
 *
 * This macro sets the PSR register to the value in @a _psr.
 */
#if defined(RTEMS_PARAVIRT)

void _SPARC_Set_PSR( uint32_t new_psr );

#define sparc_set_psr( _psr ) \
  _SPARC_Set_PSR( _psr )

#else /* RTEMS_PARAVIRT */

#define sparc_set_psr( _psr ) \
  do { \
    __asm__ volatile ( "mov  %0, %%psr " : "=r" ((_psr)) : "0" ((_psr)) ); \
    nop(); \
    nop(); \
    nop(); \
  } while ( 0 )

#endif /* RTEMS_PARAVIRT */

/**
 * @brief Macro to obtain the TBR.
 *
 * This macro returns the current contents of the TBR register in @a _tbr.
 */
#if defined(RTEMS_PARAVIRT)

uint32_t _SPARC_Get_TBR( void );

#define sparc_get_tbr( _tbr ) \
  (_tbr) = _SPARC_Get_TBR()

#else /* RTEMS_PARAVIRT */

#define sparc_get_tbr( _tbr ) \
  do { \
     (_tbr) = 0; /* to avoid unitialized warnings */ \
     __asm__ volatile( "rd %%tbr, %0" :  "=r" (_tbr) : "0" (_tbr) ); \
  } while ( 0 )

#endif /* RTEMS_PARAVIRT */

/**
 * @brief Macro to set the TBR.
 *
 * This macro sets the TBR register to the value in @a _tbr.
 */
#if defined(RTEMS_PARAVIRT)

void _SPARC_Set_TBR( uint32_t new_tbr );

#define sparc_set_tbr( _tbr ) \
  _SPARC_Set_TBR((_tbr))

#else /* RTEMS_PARAVIRT */

#define sparc_set_tbr( _tbr ) \
  do { \
     __asm__ volatile( "wr %0, 0, %%tbr" :  "=r" (_tbr) : "0" (_tbr) ); \
  } while ( 0 )

#endif /* RTEMS_PARAVIRT */

/**
 * @brief Macro to obtain the WIM.
 *
 * This macro returns the current contents of the WIM field in @a _wim.
 */
#define sparc_get_wim( _wim ) \
  do { \
    __asm__ volatile( "rd %%wim, %0" :  "=r" (_wim) : "0" (_wim) ); \
  } while ( 0 )

/**
 * @brief Macro to set the WIM.
 *
 * This macro sets the WIM field to the value in @a _wim.
 */
#define sparc_set_wim( _wim ) \
  do { \
    __asm__ volatile( "wr %0, %%wim" :  "=r" (_wim) : "0" (_wim) ); \
    nop(); \
    nop(); \
    nop(); \
  } while ( 0 )

/**
 * @brief Macro to obtain the Y register.
 *
 * This macro returns the current contents of the Y register in @a _y.
 */
#define sparc_get_y( _y ) \
  do { \
    __asm__ volatile( "rd %%y, %0" :  "=r" (_y) : "0" (_y) ); \
  } while ( 0 )

/**
 * @brief Macro to set the Y register.
 *
 * This macro sets the Y register to the value in @a _y.
 */
#define sparc_set_y( _y ) \
  do { \
    __asm__ volatile( "wr %0, %%y" :  "=r" (_y) : "0" (_y) ); \
  } while ( 0 )

/**
 * @brief SPARC disable processor interrupts.
 *
 * This method is invoked to disable all maskable interrupts.
 *
 * @return This method returns the entire PSR contents.
 */
static inline uint32_t sparc_disable_interrupts(void)
{
  register uint32_t psr __asm__("g1"); /* return value of trap handler */
  __asm__ volatile ( "ta %1\n\t" : "=r" (psr) : "i" (SPARC_SWTRAP_IRQDIS));
  return psr;
}

/**
 * @brief SPARC enable processor interrupts.
 *
 * This method is invoked to enable all maskable interrupts.
 *
 * @param[in] psr is the PSR returned by @ref sparc_disable_interrupts.
 */
static inline void sparc_enable_interrupts(uint32_t psr)
{
  register uint32_t _psr __asm__("g1") = psr; /* input to trap handler */

  /*
   * The trap instruction has a higher trap priority than the interrupts
   * according to "The SPARC Architecture Manual: Version 8", Table 7-1
   * "Exception and Interrupt Request Priority and tt Values".  Add a nop to
   * prevent a trap instruction right after the interrupt enable trap.
   */
  __asm__ volatile ( "ta %0\nnop\n" :: "i" (SPARC_SWTRAP_IRQEN), "r" (_psr));
}

/**
 * @brief SPARC exit through system call 1
 *
 * This method is invoked to go into system error halt. The optional
 * arguments can be given to hypervisor, hardware debugger, simulator or
 * similar.
 *
 * System error mode is entered when taking a trap when traps have been
 * disabled. What happens when error mode is entered depends on the motherboard.
 * In a typical development systems the CPU relingish control to the debugger,
 * simulator, hypervisor or similar. The following steps are taken:
 *
 * 1. Going into system error mode by Software Trap 0
 * 2. %g1=1 (syscall 1 - Exit)
 * 3. %g2=Primary exit code
 * 4. %g3=Secondary exit code. Dependends on %g2 exit type.
 *
 * This function never returns.
 *
 * @param[in] exitcode1 Primary exit code stored in CPU g2 register after exit
 * @param[in] exitcode2 Primary exit code stored in CPU g3 register after exit
 */
void sparc_syscall_exit(uint32_t exitcode1, uint32_t exitcode2)
  RTEMS_NO_RETURN;

/**
 * @brief SPARC flash processor interrupts.
 *
 * This method is invoked to temporarily enable all maskable interrupts.
 *
 * @param[in] _psr is the PSR returned by @ref sparc_disable_interrupts.
 */
#define sparc_flash_interrupts( _psr ) \
  do { \
    sparc_enable_interrupts( (_psr) ); \
    _psr = sparc_disable_interrupts(); \
  } while ( 0 )

/**
 * @brief SPARC obtain interrupt level.
 *
 * This method is invoked to obtain the current interrupt disable level.
 *
 * @param[in] _level is the PSR returned by @ref sparc_disable_interrupts.
 */
#define sparc_get_interrupt_level( _level ) \
  do { \
    uint32_t _psr_level = 0; \
    \
    sparc_get_psr( _psr_level ); \
    (_level) = \
      (_psr_level & SPARC_PSR_PIL_MASK) >> SPARC_PSR_PIL_BIT_POSITION; \
  } while ( 0 )

static inline uint32_t _LEON3_Get_current_processor( void )
{
  uint32_t asr17;

  __asm__ volatile (
    "rd %%asr17, %0"
    : "=&r" (asr17)
  );

  return asr17 >> LEON3_ASR17_PROCESSOR_INDEX_SHIFT;
}

#endif

#ifdef __cplusplus
}
#endif

#endif /* _RTEMS_SCORE_SPARC_H */
