/*********************************************************************
*                SEGGER MICROCONTROLLER GmbH & Co. KG                *
*        Solutions for real time microcontroller applications        *
**********************************************************************
*                                                                    *
*        (c) 2003-2008     SEGGER Microcontroller GmbH & Co KG       *
*                                                                    *
*        Internet: www.segger.com    Support:  support@segger.com    *
*                                                                    *
**********************************************************************

----------------------------------------------------------------------
File    : RTOSInit_LM3S3748.c
Purpose : Initializes and handles the hardware for the OS as far
          as required by the OS.
--------  END-OF-HEADER  ---------------------------------------------
*/

#include "RTOS.H"

/*********************************************************************
*
*       Configuration
*
*********************************************************************/

/*********************************************************************
*
*       Clock frequency settings
*/
#ifndef   OS_FSYS                   /* CPU main clock frequency      */
  #define OS_FSYS 8000000uL
#endif

#ifndef   OS_PCLK_TIMER             /* Peripheral clock for timer   */
  #define OS_PCLK_TIMER OS_FSYS     /* May vary from CPU clock      */
#endif                              /* depending on CPU             */

#ifndef   OS_PCLK_UART              /* Peripheral clock for UART    */
  #define OS_PCLK_UART OS_FSYS      /* May vary from CPU clock      */
#endif                              /* depending on CPU             */

#ifndef   OS_TICK_FREQ
  #define OS_TICK_FREQ (1000)
#endif

#ifndef   OS_USE_VARINTTABLE        /* The interrupt vector table   */
  #define OS_USE_VARINTTABLE (0)    /* may be located in RAM        */
#endif

/****** End of configuration settings *******************************/

#define OS_UART_USED ((OS_UART == 0) || (OS_UART == 1))

/*********************************************************************
*
*       Local defines (sfrs used in RTOSInit.c)
*
**********************************************************************
*/

#define _SYS_TICK_BASE_ADDR       (0xE000E010)
#define _SYS_TICK_CONTROL         (*(volatile OS_U32*)(_SYS_TICK_BASE_ADDR + 0x00))
#define _SYS_TICK_RELOAD          (*(volatile OS_U32*)(_SYS_TICK_BASE_ADDR + 0x04))
#define _SYS_TICK_VALUE           (*(volatile OS_U32*)(_SYS_TICK_BASE_ADDR + 0x08))
#define _SYS_TICK_CALIBRATION     (*(volatile OS_U32*)(_SYS_TICK_BASE_ADDR + 0x0C))

#define _SYS_TICK_ENABLE_BIT      (0)
#define _SYS_TICK_INT_ENABLE_BIT  (1)
#define _SYS_TICK_CLK_SOURCE_BIT  (2)

#define _SYS_HANDLER_STATE        (*(volatile OS_U32*)(0xE000ED24))
#define _SYS_TICK_ACT_BIT         (11)

#define _SYSCTRL_BASE_ADDR        (0x400FE000)
#define _SYSCTRL_RCGC1            (*(volatile OS_U32*) (_SYSCTRL_BASE_ADDR + 0x104))
#define _SYSCTRL_RCGC2            (*(volatile OS_U32*) (_SYSCTRL_BASE_ADDR + 0x108))
#define _SYSCTRL_RCC              (*(volatile OS_U32*)(_SYSCTRL_BASE_ADDR + 0x60))  // Run-mode clock config register
#define _SYSCTRL_MISC             (*(volatile OS_U32*)(_SYSCTRL_BASE_ADDR + 0x58))  // Interrupt status register
#define _SYSCTRL_RIS              (*(volatile OS_U32*)(_SYSCTRL_BASE_ADDR + 0x50))  // Raw interrupt status register

#define _SYSCTRL_RCC_BYPASS       (0x00000800)  // PLL bypass
#define _SYSCTRL_RCC_USE_SYSDIV   (0x00400000)  // Use sytem clock divider
#define _SYSCTRL_RCC_IOSCDIS      (0x00000002)  // Internal oscillator disable
#define _SYSCTRL_RCC_MOSCDIS      (0x00000001)  // Main oscillator disable
#define _SYSCTRL_RCC_XTAL_MASK    (0x000003C0)  // Crystal attached to main osc
#define _SYSCTRL_RCC_OSCSRC_MASK  (0x00000030)  // Oscillator input select
#define _SYSCTRL_RCC_PWRDN        (0x00002000)  // PLL power down
#define _SYSCTRL_RCC_OE           (0x00001000)  // PLL output enable
#define _SYSCTRL_INT_PLL_LOCK     (0x00000040)  // PLL lock interrupt
#define _SYSCTRL_RCC_SYSDIV_MASK  (0x07800000)  // System clock divider

#define _SYSPRI1_ADDR             (0xE000ED18)
#define _SYSHND_CTRL_ADDR         (0xE000ED24)  // System Handler Control and State
#define _SYSHND_CTRL              (*(volatile OS_U32*) (_SYSHND_CTRL_ADDR))

#define _NVIC_SYS_HND_CTRL_MEM    (0x00010000)  // Mem manage fault enable
#define _NVIC_SYS_HND_CTRL_BUS    (0x00020000)  // Bus fault enable
#define _NVIC_SYS_HND_CTRL_USAGE  (0x00040000)  // Usage fault enable

#define _NVIC_BASE_ADDR           (0xE000E000)

#define _NVIC_PRIOBASE_ADDR       (0xE000E400)
#define _NVIC_ENABLE_ADDR         (0xE000E100)
#define _NVIC_DISABLE_ADDR        (0xE000E180)
#define _NVIC_VTOREG_ADDR         (0xE000ED08)

#define _NUM_INTERRUPTS           (16+43)

/*********************************************************************
*
* The following can be used as as arguments for the PLL activation
* if required in __low_level_init()
*
**********************************************************************
*/
#define _SYSCTRL_SYSDIV_1         (0x07800000)  // Processor clock is osc/pll /1
#define _SYSCTRL_SYSDIV_4         (0x01C00000)  // Processor clock is osc/pll /4
#define _SYSCTRL_SYSDIV_10        (0x04C00000)  // Processor clock is osc/pll /10
#define _SYSCTRL_USE_PLL          (0x00000000)  // System clock is the PLL clock
#define _SYSCTRL_USE_OSC          (0x00003800)  // System clock is the osc clock
#define _SYSCTRL_XTAL_6MHZ        (0x000002C0)  // External crystal is 6MHz
#define _SYSCTRL_XTAL_8MHZ        (0x00000380)  // External crystal is 8MHz
#define _SYSCTRL_OSC_MAIN         (0x00000000)  // Oscillator source is main osc



/*****  Interrupt ID numbers ****************************************/
#define _ISR_ID_NMI               (2)           // NMI fault
#define _ISR_ID_HARD              (3)           // Hard fault
#define _ISR_ID_MPU               (4)           // MPU fault
#define _ISR_ID_BUS               (5)           // Bus fault
#define _ISR_ID_USAGE             (6)           // Usage fault
#define _ISR_ID_SVCALL            (11)          // SVCall
#define _ISR_ID_DEBUG             (12)          // Debug monitor
#define _ISR_ID_PENDSV            (14)          // PendSV
#define _ISR_ID_SYSTICK           (15)          // System Tick

#define _OS_ISR_ID_TICK           _ISR_ID_SYSTICK    // We use Sys-Timer
#define _OS_ISR_TICK_PRIO         (0xFF)             // Lowest priority

/*********************************************************************
*
*       Static data
*
**********************************************************************
*/

/*********************************************************************
*
*       Local functions
*
**********************************************************************
*/


/*********************************************************************
*
*       Global functions
*
**********************************************************************
*/

/*********************************************************************
*
*       __low_level_init()
*
* Function description
*   Called from startup code.
*   Sets the clocking to run directly from the crystal, but
*   can be used to initialize PLL as early as possible.
*   The PLL output is 200 MHz. You have to define a proper divider
*   to set the appropriate CPU speed.
*
*   For example:
*
*   (_SYSCTRL_SYSDIV_4 | _SYSCTRL_USE_PLL | _SYSCTRL_OSC_MAIN | _SYSCTRL_XTAL_8MHZ)
*   Sets the clocking to run at 50MHz from the PLL.
*
*   (_SYSCTRL_SYSDIV_10 | _SYSCTRL_USE_PLL | _SYSCTRL_OSC_MAIN | _SYSCTRL_XTAL_8MHZ)
*   Sets the clocking to run at 20MHz from the PLL.
*
*/
OS_INTERWORK int __low_level_init(void);       /* Avoid "No prototype" warning */
OS_INTERWORK int __low_level_init(void) {
  volatile unsigned long ulDelay, ulConfig;
  unsigned long ulRCC;
  //
  // Set the clocking to run directly from the crystal.
  //
  ulConfig = (_SYSCTRL_SYSDIV_1 | _SYSCTRL_USE_OSC | _SYSCTRL_OSC_MAIN | _SYSCTRL_XTAL_8MHZ);
  //
  // Get the current value of the RCC register.
  //
  ulRCC = _SYSCTRL_RCC;
  //
  // Bypass the PLL and system clock dividers for now.
  //
  ulRCC |= _SYSCTRL_RCC_BYPASS;
  ulRCC &= ~(_SYSCTRL_RCC_USE_SYSDIV);
  //
  // Write the new RCC value.
  //
  _SYSCTRL_RCC = ulRCC;
  //
  // Make sure that the PLL and system clock dividers are bypassed for now.
  //
  ulRCC |= _SYSCTRL_RCC_BYPASS;
  ulRCC &= ~(_SYSCTRL_RCC_USE_SYSDIV);
  //
  // Make sure that the required oscillators are enabled.  For now, the
  // previously enabled oscillators must be enabled along with the newly
  // requested oscillators.
  //
  ulRCC &= (~(_SYSCTRL_RCC_IOSCDIS | _SYSCTRL_RCC_MOSCDIS) |
            (ulConfig & (_SYSCTRL_RCC_IOSCDIS | _SYSCTRL_RCC_MOSCDIS)));
  //
  // Set the new crystal value, oscillator source, and PLL configuration.
  //
  ulRCC &= ~(_SYSCTRL_RCC_XTAL_MASK | _SYSCTRL_RCC_OSCSRC_MASK |
             _SYSCTRL_RCC_PWRDN | _SYSCTRL_RCC_OE);
  ulRCC |= ulConfig & (_SYSCTRL_RCC_XTAL_MASK | _SYSCTRL_RCC_OSCSRC_MASK |
                       _SYSCTRL_RCC_PWRDN | _SYSCTRL_RCC_OE);
  //
  // Clear the PLL lock interrupt.
  //
  _SYSCTRL_MISC = _SYSCTRL_INT_PLL_LOCK;
  //
  // Write the new RCC value.
  //
  _SYSCTRL_RCC = ulRCC;
  //
  // Wait for a bit so that new crystal value and oscillator source can take
  // effect.  One of the oscillators may need to be started as well.
  //
  for(ulDelay = 0; ulDelay < 16; ulDelay++) {
  }
  //
  // Disable the appropriate oscillators.
  //
  ulRCC &= ~(_SYSCTRL_RCC_IOSCDIS | _SYSCTRL_RCC_MOSCDIS);
  ulRCC |= ulConfig & (_SYSCTRL_RCC_IOSCDIS | _SYSCTRL_RCC_MOSCDIS);
  //
  // Write the new RCC value.
  //
  _SYSCTRL_RCC = ulRCC;
  //
  // Set the requested system divider.  This will not get written
  // immediately.
  //
  ulRCC &= ~(_SYSCTRL_RCC_SYSDIV_MASK | _SYSCTRL_RCC_USE_SYSDIV);
  ulRCC |= ulConfig & (_SYSCTRL_RCC_SYSDIV_MASK | _SYSCTRL_RCC_USE_SYSDIV);
  //
  // See if the PLL output is being used to clock the system.
  //
  if(!(ulConfig & _SYSCTRL_RCC_BYPASS)) {
    //
    // Wait until the PLL has locked.
    //
    for(ulDelay = 32768; ulDelay > 0; ulDelay--) {
      if(_SYSCTRL_RIS & _SYSCTRL_INT_PLL_LOCK) {
        break;
      }
    }
    //
    // Enable use of the PLL.
    //
    ulRCC &= ~(_SYSCTRL_RCC_BYPASS);
  }
  //
  // Write the final RCC value.
  //
  _SYSCTRL_RCC = ulRCC;
  //
  // Delay for a little bit so that the system divider takes effect.
  //
  for(ulDelay = 0; ulDelay < 16; ulDelay++) {
  }
  return 1;                       // Always initialize segments !
};

/*********************************************************************
*
*       OS_Systick
*
* Function description
*   This is the code that gets called when the processor receives a
*   _SysTick exception. SysTick is used as OS timer tick.
*
* NOTES:
*   (1) It has to be inserted in the interrupt vector table, if RAM
*       vectors are not used. Therefore is is declared public
*/
void OS_Systick(void) {
  OS_EnterNestableInterrupt();
  OS_HandleTick();
  OS_LeaveNestableInterrupt();
}

/*********************************************************************
*
*       OS_InitHW()
*
*       Initialize the hardware (timer) required for the OS to run.
*       May be modified, if an other timer should be used
*/
#define _TICK_RELOAD (OS_PCLK_TIMER / OS_TICK_FREQ - 1)
void OS_InitHW(void) {
  OS_DI();
  //
  // Initialize OS timer, clock soure = core clock
  //
  _SYS_TICK_RELOAD  = _TICK_RELOAD;
  _SYS_TICK_CONTROL = (1 << _SYS_TICK_ENABLE_BIT) | (1 << _SYS_TICK_CLK_SOURCE_BIT);
  //
  // Install Systick Timer Handler and enable timer interrupt
  //
  OS_ARM_InstallISRHandler(_OS_ISR_ID_TICK, (OS_ISR_HANDLER*)OS_Systick);
  OS_ARM_ISRSetPrio(_OS_ISR_ID_TICK, _OS_ISR_TICK_PRIO);
  OS_ARM_EnableISR(_OS_ISR_ID_TICK);

  OS_COM_Init();
  OS_RestoreI();
}

/*********************************************************************
*
*       Idle loop  (OS_Idle)
*
*       Please note:
*       This is basically the "core" of the idle loop.
*       This core loop can be changed, but:
*       The idle loop does not have a stack of its own, therefore no
*       functionality should be implemented that relies on the stack
*       to be preserved. However, a simple program loop can be programmed
*       (like toggeling an output or incrementing a counter)
*/
void OS_Idle(void) {     // Idle loop: No task is ready to execute
  while (1) {
  }
}

/*********************************************************************
*
*       Get time [cycles]
*
*       This routine is required for high
*       resolution time maesurement functions.
*       It returns the system time in timer clock cycles.
*/
OS_U32 OS_GetTime_Cycles(void) {
  unsigned int t_cnt;
  OS_U32 time;

  time  = OS_Time + 1;
  t_cnt = _SYS_TICK_VALUE;
  if (_SYS_HANDLER_STATE & (1 << _SYS_TICK_ACT_BIT)) {   /* if timer interrupt pending */
    t_cnt = _SYS_TICK_VALUE;                             /* Adjust result              */
    time++;
  }
  return (OS_PCLK_TIMER/1000)*time - t_cnt;
}

/*********************************************************************
*
*       OS_ConvertCycles2us
*
*       Convert Cycles into micro seconds.
*
*       If your clock frequency is not a multiple of 1 MHz,
*       you may have to modify this routine in order to get proper
*       diagonstics.
*
*       This routine is required for profiling or high resolution time
*       measurement only. It does not affect operation of the OS.
*/
OS_U32 OS_ConvertCycles2us(OS_U32 Cycles) {
  return Cycles/(OS_PCLK_TIMER/1000000);
}

/*********************************************************************
*
*       Communication for embOSView not selected
*
**********************************************************************
*/
void OS_COM_Init(void) {}
void OS_COM_Send1(OS_U8 c) {
  OS_USEPARA(c);
  OS_COM_ClearTxActive();  /* Free OS transmitter buffer */
}


/*********************************************************************
*
*       OS_COM_IsrHandler
*
* Function description
*   The communication interrupt handler for UART communication
*   to embOSView.
*
* NOTES:
*   (1) It has to be inserted in the interrupt vector table, if RAM
*       vectors are not used. Therefore is is declared public
*/
void OS_COM_IsrHandler(void) {
  while(1);
}

/*********************************************************************
*
*       OS interrupt handler and ISR specific functions
*
**********************************************************************
*/

#if OS_USE_VARINTTABLE
//
// The interrupt vector table may be located anywhere in RAM
//
#pragma segment="VTABLE"
#pragma data_alignment=256
__no_init void (*g_pfnRAMVectors[_NUM_INTERRUPTS])(void) @ "VTABLE";

#define VTBASE_IN_RAM_BIT             (29)
#define _RAM_START_ADDR               (0x20000000)
#endif

/*********************************************************************
*
*       OS_ARM_InstallISRHandler
*/
OS_ISR_HANDLER* OS_ARM_InstallISRHandler (int ISRIndex, OS_ISR_HANDLER* pISRHandler) {
#if OS_USE_VARINTTABLE
  OS_ISR_HANDLER*  pOldHandler;
  OS_U32 ulIdx;

  pOldHandler = NULL;
  //
  // Check whether the RAM vector table has been initialized.
  //
  if ((*(OS_U32*)_NVIC_VTOREG_ADDR) != (unsigned long)g_pfnRAMVectors) {
    //
    // Copy the vector table from the beginning of FLASH to the RAM vector table.
    //
    for(ulIdx = 0; ulIdx < _NUM_INTERRUPTS; ulIdx++) {
      g_pfnRAMVectors[ulIdx] = (void (*)(void))(*((volatile unsigned long *)(ulIdx * 4)));
    }
    //
    // Program NVIC to point at the RAM vector table.
    //
    *(OS_U32*)_NVIC_VTOREG_ADDR = ((OS_U32)__sfb("VTABLE") - _RAM_START_ADDR) | (1 << VTBASE_IN_RAM_BIT);
  }
  //
  // Save the interrupt handler.
  //
  pOldHandler = g_pfnRAMVectors[ISRIndex];
  g_pfnRAMVectors[ISRIndex] = pISRHandler;
  return (pOldHandler);
#else
  return (NULL);
#endif
}

/*********************************************************************
*
*       OS_ARM_EnableISR
*/
void OS_ARM_EnableISR(int ISRIndex) {
  OS_DI();
  if (ISRIndex < _NUM_INTERRUPTS) {
    if (ISRIndex >= 16) {
      //
      // Enable standard "external" interrupts, starting at index 16
      //
      ISRIndex -= 16;
      *(((OS_U32*) _NVIC_ENABLE_ADDR) + (ISRIndex >> 5)) = (1 << (ISRIndex & 0x1F));
    } else if (ISRIndex == _ISR_ID_MPU) {
      //
      // Enable the MemManage interrupt.
      //
      _SYSHND_CTRL |= _NVIC_SYS_HND_CTRL_MEM;
    } else if (ISRIndex == _ISR_ID_BUS) {
      //
      // Enable the bus fault interrupt.
      //
      _SYSHND_CTRL |= _NVIC_SYS_HND_CTRL_BUS;
    } else if (ISRIndex == _ISR_ID_USAGE) {
      //
      // Enable the usage fault interrupt.
      //
      _SYSHND_CTRL |= _NVIC_SYS_HND_CTRL_USAGE;
    } else if (ISRIndex == _ISR_ID_SYSTICK) {
      //
      // Enable the System Tick interrupt.
      //
      _SYS_TICK_CONTROL |= (1 << _SYS_TICK_INT_ENABLE_BIT);
    }
  }
  OS_RestoreI();
}

/*********************************************************************
*
*       OS_ARM_DisableISR
*/
void OS_ARM_DisableISR(int ISRIndex) {
  OS_DI();
  if (ISRIndex < _NUM_INTERRUPTS) {
    if (ISRIndex >= 16) {
      //
      // Disable standard "external" interrupts
      //
      ISRIndex -= 16;
      *(((OS_U32*) _NVIC_DISABLE_ADDR) + (ISRIndex >> 5)) = (1 << (ISRIndex & 0x1F));
    } else if (ISRIndex == _ISR_ID_MPU) {
      //
      // Disable the MemManage interrupt.
      //
      _SYSHND_CTRL &= ~_NVIC_SYS_HND_CTRL_MEM;
    } else if (ISRIndex == _ISR_ID_BUS) {
      //
      // Disable the bus fault interrupt.
      //
      _SYSHND_CTRL &= ~_NVIC_SYS_HND_CTRL_BUS;
    } else if (ISRIndex == _ISR_ID_USAGE) {
      //
      // Disable the usage fault interrupt.
      //
      _SYSHND_CTRL &= ~_NVIC_SYS_HND_CTRL_USAGE;
    } else if (ISRIndex == _ISR_ID_SYSTICK) {
      //
      // Enable the System Tick interrupt.
      //
      _SYS_TICK_CONTROL &= ~(1 << _SYS_TICK_INT_ENABLE_BIT);
    }
  }
  OS_RestoreI();
}

/*********************************************************************
*
*       OS_ARM_ISRSetPrio
*
*   Notes:
*     (1) Some priorities of system handler are reserved
*         0..3 : Priority can not be set
*         7..10: Reserved
*         13   : Reserved
*     (2) System handler use different control register. This affects
*         ISRIndex 0..15
*/
int OS_ARM_ISRSetPrio(int ISRIndex, int Prio) {
  OS_U8* pPrio;
  int    OldPrio;

  OldPrio = 0;
  if (ISRIndex < _NUM_INTERRUPTS) {
    if (ISRIndex >= 16) {
      //
      // Handle standard "external" interrupts
      //
      ISRIndex -= 16;                   // Adjust index
      OS_DI();
      pPrio = (OS_U8*)(_NVIC_PRIOBASE_ADDR + ISRIndex);
      OldPrio = *pPrio;
      *pPrio = Prio;
      OS_RestoreI();
    } else {
      //
      // Handle System Interrupt controller
      //
      if ((ISRIndex < 4) | ((ISRIndex >= 7) && (ISRIndex <= 10)) | (ISRIndex == 13)) {
        //
        // Reserved ISR channel, do nothing
        //
      } else {
        //
        // Set priority in system interrupt priority control register
        //
        OS_DI();
        pPrio = (OS_U8*)(_SYSPRI1_ADDR);
        ISRIndex -= 4;                  // Adjust Index
        OldPrio = pPrio[ISRIndex];
        pPrio[ISRIndex] = Prio;
        OS_RestoreI();
      }
    }
  }
  return OldPrio;
}

/*****  EOF  ********************************************************/
