/**
 * @file
 * 
 * @brief Motorola M68K CPU Dependent Source
 * 
 * @addtogroup RTEMSScoreCPUm68k
 * 
 * This include file contains information pertaining to the Motorola
 * m68xxx processor family.
 */

/*
 *  COPYRIGHT (c) 1989-1999.
 *  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_M68K_H
#define _RTEMS_SCORE_M68K_H

#ifdef __cplusplus
extern "C" {
#endif

/*
 *  This section contains the information required to build
 *  RTEMS for a particular member of the Motorola MC68xxx
 *  family.  It does this by setting variables to indicate
 *  which implementation dependent features are present in
 *  a particular member of the family.
 *
 *  Currently recognized:
 *     -m68000
 *     -m68000 -msoft-float
 *     -m68020
 *     -m68020 -msoft-float
 *     -m68030
 *     -m68040 -msoft-float
 *     -m68040
 *     -m68040 -msoft-float
 *     -m68060
 *     -m68060 -msoft-float
 *     -m68302        (no FP) (deprecated, use -m68000)
 *     -m68332        (no FP) (deprecated, use -mcpu32)
 *     -mcpu32        (no FP)
 *     -m5200         (no FP)
 *     -m528x         (no FP, ISA A+)
 *
 *  As of gcc 2.8.1 and egcs 1.1, there is no distinction made between
 *  the CPU32 and CPU32+.  The option -mcpu32 generates code which can
 *  be run on either core.  RTEMS distinguishes between these two cores
 *  because they have different alignment rules which impact performance.
 *  If you are using a CPU32+, then the symbol RTEMS__mcpu32p__ should
 *  be defined in your custom file (see make/custom/gen68360.cfg for an
 *  example of how to do this.  If gcc ever distinguishes between these
 *  two cores, then RTEMS__mcpu32p__ usage will be replaced with the
 *  appropriate compiler defined predefine.
 *
 *  Here is some information on the 040 variants (courtesy of Doug McBride,
 *  mcbride@rodin.colorado.edu):
 *
 *    "The 68040 is a superset of the 68EC040 and the 68LC040.  The
 *    68EC040 and 68LC040 do not have FPU's.  The 68LC040 and the
 *    68EC040 have renamed the DLE pin as JS0 which must be tied to
 *    Gnd or Vcc. The 68EC040 has renamed the MDIS pin as JS1.  The
 *    68EC040 has access control units instead of memory management units.
 *    The 68EC040 should not have the PFLUSH or PTEST instructions executed
 *    (cause an indeterminate result).  The 68EC040 and 68LC040 do not
 *    implement the DLE or multiplexed bus modes.  The 68EC040 does not
 *    implement the output buffer impedance selection mode of operation."
 *
 *  M68K_HAS_EXTB_L is used to enable/disable usage of the extb.l instruction
 *  which is not available for 68000 or 68ec000 cores (68000, 68001, 68008,
 *  68010, 68302, 68306, 68307).  This instruction is available on the 68020
 *  up and the cpu32 based models.
 *
 *  M68K_HAS_MISALIGNED is non-zero if the CPU allows byte-misaligned
 *  data access (68020, 68030, 68040, 68060, CPU32+).
 *
 *  NOTE:
 *    Eventually it would be nice to evaluate doing a lot of this section
 *    by having each model specify which core it uses and then go from there.
 */

/*
 * Handle the Coldfire family based on the instruction set.
 */
#if defined(__mcoldfire__)

# define CPU_NAME "Motorola ColdFire"

# if defined(__mcfisaa__)
/* Motorola ColdFire ISA A */
# define CPU_MODEL_NAME         "mcfisaa"
# define M68K_HAS_VBR             1
# define M68K_HAS_BFFFO           0
# define M68K_HAS_SEPARATE_STACKS 0
# define M68K_HAS_PREINDEXING     0
# define M68K_HAS_EXTB_L          1
# define M68K_HAS_MISALIGNED      1

# elif defined(__mcfisaaplus__)
/* Motorola ColdFire ISA A+ */
# define CPU_MODEL_NAME         "mcfisaaplus"
# define M68K_HAS_VBR             1
# define M68K_HAS_BFFFO           0
# define M68K_HAS_SEPARATE_STACKS 0
# define M68K_HAS_PREINDEXING     0
# define M68K_HAS_EXTB_L          1
# define M68K_HAS_MISALIGNED      1

# elif defined(__mcfisab__)
/* Motorola ColdFire ISA B */
# define CPU_MODEL_NAME         "mcfisab"
# define M68K_HAS_VBR             1
# define M68K_HAS_BFFFO           0
# define M68K_HAS_SEPARATE_STACKS 0
# define M68K_HAS_PREINDEXING     0
# define M68K_HAS_EXTB_L          1
# define M68K_HAS_MISALIGNED      1

# else
# error "Unsupported Coldfire ISA -- Please notify RTEMS"
# endif

/*
 * Assume the FPU support is independent. I think it is just the ISA B
 * instruction set.
 */
# if defined (__mcffpu__)
# define M68K_HAS_FPU            1
  /*
   * td: can we be sure that all CFs with FPU also have an EMAC?
   */
# define M68K_HAS_EMAC           1
# define M68K_HAS_FPSP_PACKAGE   0
# else
# define M68K_HAS_FPU            0
# define M68K_HAS_FPSP_PACKAGE   0
# endif

/*
 * Tiny RTEMS support. Small stack and limited priorities.
 *
 * These CPUs have very limited on-CPU memory which cannot
 * be expanded. We have to be gentle with them or nothing
 * will every run.
 */
# if (defined(__mcf_cpu_52221) || \
      defined(__mcf_cpu_52223) || \
      defined(__mcf_cpu_52230) || \
      defined(__mcf_cpu_52231) || \
      defined(__mcf_cpu_52232) || \
      defined(__mcf_cpu_52233) || \
      defined(__mcf_cpu_52234) || \
      defined(__mcf_cpu_52235) || \
      defined(__mcf_cpu_52225) || \
      defined(__mcf_cpu_52235))
  #define M68K_CPU_STACK_MINIMUM_SIZE 1024
  /* Define the lowest priority. Based from 0 to this is 16 levels. */
  #define M68K_CPU_PRIORITY_MAXIMUM   15
# else
  #define M68K_CPU_STACK_MINIMUM_SIZE 4096
  /* Use the default number of priorities */
  #define M68K_CPU_PRIORITY_MAXIMUM   255
# endif

#else

/*
 *  Figure out all CPU Model Feature Flags based upon compiler
 *  predefines.   Notice the only exception to this is that
 *  gcc does not distinguish between CPU32 and CPU32+.  This
 *  feature selection logic is setup such that if RTEMS__mcpu32p__
 *  is defined, then CPU32+ rules are used.  Otherwise, the safe
 *  but less efficient CPU32 rules are used for the CPU32+.
 */

# define CPU_NAME "Motorola MC68xxx"

/*
 * One stack size fits all 68000 processors.
 */
# define M68K_CPU_STACK_MINIMUM_SIZE 4096

# if (defined(__mc68020__) && !defined(__mcpu32__))

# define CPU_MODEL_NAME          "m68020"
# define M68K_HAS_VBR             1
# define M68K_HAS_SEPARATE_STACKS 1
# define M68K_HAS_BFFFO           1
# define M68K_HAS_PREINDEXING     1
# define M68K_HAS_EXTB_L          1
# define M68K_HAS_MISALIGNED      1
#  if defined (__HAVE_68881__)
#  define M68K_HAS_FPU            1
#  define M68K_HAS_FPSP_PACKAGE   0
#  else
#  define M68K_HAS_FPU            0
#  define M68K_HAS_FPSP_PACKAGE   0
#  endif

# elif defined(__mc68030__)

# define CPU_MODEL_NAME          "m68030"
# define M68K_HAS_VBR             1
# define M68K_HAS_SEPARATE_STACKS 1
# define M68K_HAS_BFFFO           1
# define M68K_HAS_PREINDEXING     1
# define M68K_HAS_EXTB_L          1
# define M68K_HAS_MISALIGNED      1
#  if defined (__HAVE_68881__)
#  define M68K_HAS_FPU            1
#  define M68K_HAS_FPSP_PACKAGE   0
#  else
#  define M68K_HAS_FPU            0
#  define M68K_HAS_FPSP_PACKAGE   0
#  endif

# elif defined(__mc68040__)

# define CPU_MODEL_NAME          "m68040"
# define M68K_HAS_VBR             1
# define M68K_HAS_SEPARATE_STACKS 1
# define M68K_HAS_BFFFO           1
# define M68K_HAS_PREINDEXING     1
# define M68K_HAS_EXTB_L          1
# define M68K_HAS_MISALIGNED      1
#  if defined (__HAVE_68881__)
#  define M68K_HAS_FPU            1
#  define M68K_HAS_FPSP_PACKAGE   1
#  else
#  define M68K_HAS_FPU            0
#  define M68K_HAS_FPSP_PACKAGE   0
#  endif

# elif defined(__mc68060__)

# define CPU_MODEL_NAME          "m68060"
# define M68K_HAS_VBR             1
# define M68K_HAS_SEPARATE_STACKS 0
# define M68K_HAS_BFFFO           1
# define M68K_HAS_PREINDEXING     1
# define M68K_HAS_EXTB_L          1
# define M68K_HAS_MISALIGNED      1
#  if defined (__HAVE_68881__)
#  define M68K_HAS_FPU            1
#  define M68K_HAS_FPSP_PACKAGE   0
#  else
#  define M68K_HAS_FPU            0
#  define M68K_HAS_FPSP_PACKAGE   0
#  endif

# elif defined(__mc68302__)

# define CPU_MODEL_NAME          "m68302"
# define M68K_HAS_VBR             0
# define M68K_HAS_SEPARATE_STACKS 0
# define M68K_HAS_BFFFO           0
# define M68K_HAS_PREINDEXING     0
# define M68K_HAS_EXTB_L          0
# define M68K_HAS_MISALIGNED      0
# define M68K_HAS_FPU             0
# define M68K_HAS_FPSP_PACKAGE    0

  /* gcc and egcs do not distinguish between CPU32 and CPU32+ */
# elif defined(RTEMS__mcpu32p__)

# define CPU_MODEL_NAME          "mcpu32+"
# define M68K_HAS_VBR             1
# define M68K_HAS_SEPARATE_STACKS 0
# define M68K_HAS_BFFFO           0
# define M68K_HAS_PREINDEXING     1
# define M68K_HAS_EXTB_L          1
# define M68K_HAS_MISALIGNED      1
# define M68K_HAS_FPU             0
# define M68K_HAS_FPSP_PACKAGE    0

# elif defined(__mcpu32__)

# define CPU_MODEL_NAME          "mcpu32"
# define M68K_HAS_VBR             1
# define M68K_HAS_SEPARATE_STACKS 0
# define M68K_HAS_BFFFO           0
# define M68K_HAS_PREINDEXING     1
# define M68K_HAS_EXTB_L          1
# define M68K_HAS_MISALIGNED      0
# define M68K_HAS_FPU             0
# define M68K_HAS_FPSP_PACKAGE    0

# elif defined(__mc68000__)

# define CPU_MODEL_NAME          "m68000"
# define M68K_HAS_VBR             0
# define M68K_HAS_SEPARATE_STACKS 0
# define M68K_HAS_BFFFO           0
# define M68K_HAS_PREINDEXING     0
# define M68K_HAS_EXTB_L          0
# define M68K_HAS_MISALIGNED      0
#  if defined (__HAVE_68881__)
#  define M68K_HAS_FPU            1
#  define M68K_HAS_FPSP_PACKAGE   0
#  else
#  define M68K_HAS_FPU            0
#  define M68K_HAS_FPSP_PACKAGE   0
#  endif

# else

# error "Unsupported 68000 CPU model -- are you sure you're running a 68k compiler?"

# endif

/*
 * No Tiny RTEMS support on the standard 68000 family.
 */
# define M68K_CPU_STACK_MINIMUM_SIZE 4096
# define M68K_CPU_PRIORITY_MAXIMUM   255

#endif

/*
 * OBSOLETE: Backward compatibility only - Don't use.
 * Use __mcoldfire__ instead.
 */
#if defined(__mcoldfire__)
#define M68K_COLDFIRE_ARCH  1
#else
#define M68K_COLDFIRE_ARCH	0
#endif

#ifndef ASM

#if ( defined(__mcoldfire__) )
#define m68k_disable_interrupts( _level ) \
   do { uint32_t   _tmpsr = 0x0700; \
        __asm__ volatile ( "move.w %%sr,%0\n\t" \
 		       "or.l   %0,%1\n\t" \
 		       "move.w %1,%%sr" \
 		       : "=d" (_level), "=d"(_tmpsr) : "1"(_tmpsr) \
               : "cc" ); \
   } while( 0 )
#else
#define m68k_disable_interrupts( _level ) \
  __asm__ volatile ( "move.w  %%sr,%0\n\t" \
                 "or.w    #0x0700,%%sr" \
                    : "=d" (_level) \
                    : : "cc" )
#endif

#define m68k_enable_interrupts( _level ) \
  __asm__ volatile ( "move.w  %0,%%sr " : : "d" (_level) : "cc");

#if ( defined(__mcoldfire__) )
#define m68k_flash_interrupts( _level ) \
   do { uint32_t   _tmpsr = 0x0700; \
	asm volatile ( "move.w %2,%%sr\n\t" \
		       "or.l   %2,%1\n\t" \
		       "move.w %1,%%sr" \
		       : "=d"(_tmpsr) : "0"(_tmpsr), "d"(_level) \
               : "cc"); \
   } while( 0 )
#else
#define m68k_flash_interrupts( _level ) \
  __asm__ volatile ( "move.w  %0,%%sr\n\t" \
                 "or.w    #0x0700,%%sr" \
                    : : "d" (_level) \
                    : "cc" )
#endif

#define m68k_get_interrupt_level( _level ) \
  do { \
    uint32_t   _tmpsr; \
    \
    __asm__ volatile( "move.w %%sr,%0" : "=d" (_tmpsr)); \
    _level = (_tmpsr & 0x0700) >> 8; \
  } while (0)

#define m68k_set_interrupt_level( _newlevel ) \
  do { \
    uint32_t   _tmpsr; \
    \
    __asm__ volatile( "move.w  %%sr,%0" : "=d" (_tmpsr)); \
    _tmpsr = (_tmpsr & 0xf8ff) | ((_newlevel) << 8); \
    __asm__ volatile( "move.w  %0,%%sr" : : "d" (_tmpsr)); \
  } while (0)

#if ( M68K_HAS_VBR == 1 && !defined(__mcoldfire__) )
#define m68k_get_vbr( vbr ) \
  __asm__ volatile ( "movec   %%vbr,%0 " : "=r" (vbr))

#define m68k_set_vbr( vbr ) \
  __asm__ volatile ( "movec   %0,%%vbr " : : "r" (vbr))

#elif ( defined(__mcoldfire__) )
extern void*                     _VBR;
#define m68k_get_vbr( _vbr ) _vbr = &_VBR

#define m68k_set_vbr( _vbr ) \
  do { \
    __asm__ volatile ( "movec   %0,%%vbr " : : "r" (_vbr)); \
    _VBR = (void *)_vbr; \
  } while(0)

#else
#define m68k_get_vbr( _vbr ) _vbr = (void *)_VBR
#define m68k_set_vbr( _vbr )
#endif

/*
 *  Access Control Registers
 */
#define m68k_set_cacr(_cacr) __asm__ volatile ("movec %0,%%cacr" : : "d" (_cacr))
#define m68k_set_acr0(_acr0) __asm__ volatile ("movec %0,%%acr0" : : "d" (_acr0))
#define m68k_set_acr1(_acr1) __asm__ volatile ("movec %0,%%acr1" : : "d" (_acr1))

/*
 *  The following routine swaps the endian format of an unsigned int.
 *  It must be static because it is referenced indirectly.
 */
#if ( defined(__mcoldfire__) )

/* There are no rotate commands in Coldfire architecture. We will use
 * generic implementation of endian swapping for Coldfire.
 */
static inline uint32_t m68k_swap_u32(
  uint32_t value
  )
{
  uint32_t   byte1, byte2, byte3, byte4, swapped;

  byte4 = (value >> 24) & 0xff;
  byte3 = (value >> 16) & 0xff;
  byte2 = (value >> 8)  & 0xff;
  byte1 =  value        & 0xff;

  swapped = (byte1 << 24) | (byte2 << 16) | (byte3 << 8) | byte4;
  return( swapped );
}

static inline uint16_t m68k_swap_u16(
  uint16_t value
)
{
  return (((value & 0xff) << 8) | ((value >> 8) & 0xff));
}

#else

static inline uint32_t m68k_swap_u32(
  uint32_t value
)
{
  uint32_t swapped = value;

  __asm__ volatile( "rorw  #8,%0" : "=d" (swapped) : "0" (swapped) );
  __asm__ volatile( "swap  %0"    : "=d" (swapped) : "0" (swapped) );
  __asm__ volatile( "rorw  #8,%0" : "=d" (swapped) : "0" (swapped) );

  return( swapped );
}

static inline uint16_t m68k_swap_u16(
  uint16_t value
)
{
  uint16_t swapped = value;

  __asm__ volatile( "rorw  #8,%0" : "=d" (swapped) : "0" (swapped) );

  return( swapped );
}
#endif

#define CPU_swap_u32( value )  m68k_swap_u32( value )
#define CPU_swap_u16( value )  m68k_swap_u16( value )


/*
 *  _CPU_virtual_to_physical
 *
 *  This function is used to map virtual addresses to physical
 *  addresses.
 *
 *  FIXME: ASSUMES THAT VIRTUAL ADDRESSES ARE THE SAME AS THE
 *  PHYSICAL ADDRESSES
 */
static inline void * _CPU_virtual_to_physical (
  const void * d_addr )
{
  return (void *) d_addr;
}


#endif  /* !ASM */

#ifdef __cplusplus
}
#endif

#endif /* _RTEMS_SCORE_M68K_H */
