/**HEADER********************************************************************
* 
* Copyright (c) 2012 Freescale Semiconductor;
* All Rights Reserved
*
*************************************************************************** 
*
* THIS SOFTWARE IS PROVIDED BY FREESCALE "AS IS" AND ANY EXPRESSED OR 
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  
* IN NO EVENT SHALL FREESCALE OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, 
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING 
* IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 
* THE POSSIBILITY OF SUCH DAMAGE.
*
**************************************************************************
*
* $FileName: init_bsp.c$
* $Version : 1.0.0.0$
* $Date    : Nov-10-2010$
*
* Comments:
*
*   This file contains the source functions for functions required to
*   specifically initialize the card.
*
*END************************************************************************/

#include "mqx_inc.h"
#include "bsp.h"
#include "bsp_prv.h"



const char _PTR_ _mqx_bsp_revision = REAL_NUM_TO_STR(BSP_REVISION);
const char _PTR_ _mqx_io_revision  = REAL_NUM_TO_STR(IO_REVISION);

static uint_32 _bsp_get_hwticks(pointer param);
static void    _bsp_setup_watchdog(void);

extern boolean _bsp_timer_sw_prescaller_check();

#ifdef DEBUG_ENABLE_ILLEGAL_INSTR_ISR
extern void _bsp_timer_isr_IllegalInstr(pointer);
#endif



interrupt void int_nmi_handler(void);
interrupt void frameerr_nmi_handler(void);
interrupt void swi7_nmi_handler(void);

volatile uint_8 gNmiSourceFlag = 0;



/*FUNCTION*-------------------------------------------------------------------
*
* Function Name    : _get_nmi_source
* Returned Value   : byte value
*                     b[INT_NMI_BIT] - 1 indicates INT caused an nmi interrupt
*                     b[FRAMEERR_NMI_BIT] - 1 indicates frame error caused an nmi interrupt
*                     b[SW7_NMI_BIT] - 1 indicates  sw level 7 caused an nmi interrupt
* Comments         :
*   This function returns a byte value that indicates the source(s) that caused
*   the nmi interrupt.  Note that the value may have multiple bits set to indicate 
*   that more than one interrupt source generated the nmi interrupt.
*   
*   The user can call this function in the software level 6 interrupt service
*   routine to determine the cause of the nmi to handle it properly.  See function
*   _clear_nmi_source() to clear the nmi source.
*
*END*----------------------------------------------------------------------*/
__declspec(register_abi) uint_8 _get_nmi_source(void)
{
  return gNmiSourceFlag;
}


/*FUNCTION*-------------------------------------------------------------------
*
* Function Name    : _clear_nmi_source
* Parm Values      : 
*                   nmi_bit - bit number of nmi source to clear
*                     INT_NMI_BIT to clear INT nmi source
*                     FRAMEERR_NMI_BIT  to clear frame error nmi source
*                     SW7_NMI_BIT to clear sw level 7 nmi source
* Returned Value   :
* Comments         :
*   User calls this function to clear the source of the nmi.  
*   
*   IMPORTANT: Only one source can be cleared for each function call.
*
*END*----------------------------------------------------------------------*/
__declspec(register_abi) void _clear_nmi_source(uint_8 nmi_bit)
{
  asm {
    // Set bit to indicate INT interrupt.
    lea     gNmiSourceFlag, a0
    bclr    d0, (a0)
  }
}


/*FUNCTION*-------------------------------------------------------------------
*
* Function Name    : _clear_all_nmi_source
* Returned Value   :
* Comments         :
*   User calls this function to clear ALL the nmi sources.  This function is
*   typically called during initialization.
*
*END*----------------------------------------------------------------------*/
__declspec(register_abi) void _clear_all_nmi_source(void)
{
  asm {
    // Set bit to indicate INT interrupt.
    lea     gNmiSourceFlag, a0
    bclr    #INT_NMI_BIT, (a0)
    bclr    #FRAMEERR_NMI_BIT, (a0)
    bclr    #SW7_NMI_BIT, (a0)
  }
}



/*FUNCTION*-------------------------------------------------------------------
*
* Function Name    : int_nmi_handler
* Returned Value   : 
* Comments         :
* 
*   This function is the isr for the external INT interrupt.  It does the
*   following:
*   
*   - Clears the INT interrupt in the hardware register.
*   - Sets a bit in the nmi source flag.
*   - Generates a software level 6 interrupt.
*   
*END*----------------------------------------------------------------------*/
interrupt void int_nmi_handler(void)
{
    
  asm {
    
    // Clear INT interrupt.
    mov3q   #2, d0
    bset    d0, 0xFFFF80C0          // IRQSC_IRQACK = 1

    // Set bit to indicate INT interrupt.
    lea     gNmiSourceFlag, a0
    bset    #INT_NMI_BIT, (a0)
    
    // Force level 6 sw interrupt
    moveq   #0x39,d0
    move.b  d0, 0xFFFFFFDE          // INTC_SFRC = 0x39
    
  }
  
}

/*FUNCTION*-------------------------------------------------------------------
*
* Function Name    : frameerr_nmi_handler
* Returned Value   : 
* Comments         :
* 
*   This function is the isr for the frame error interrupt.  It does the
*   following:
*   
*   - Clears the frame error interrupt in the hardware register.
*   - Sets a bit in the nmi source flag.
*   - Generates a software level 6 interrupt.
*
*END*----------------------------------------------------------------------*/
interrupt void frameerr_nmi_handler(void)
{

  asm {
    
    // Clear frame error interrupt.
    mov3q   #4, d0
    bset    d0, 0xFFFF8061          // FCSR |= (1 << 2)
        
    // Set bit to indicate frame error interrupt.
    lea     gNmiSourceFlag, a0
    bset.b  #FRAMEERR_NMI_BIT, (a0)
    
    // Force level 6 sw interrupt
    moveq   #0x39,d0
    move.b  d0, 0xFFFFFFDE          // INTC_SFRC = 0x39
    
  }
  
}

/*FUNCTION*-------------------------------------------------------------------
*
* Function Name    : swi7_nmi_handler
* Returned Value   : 
* Comments         :
*   This function is the isr for software level 7 interrupt.
* 
*   This function is the isr for the software level 7 (swi7) interrupt.  It
*   does the following:
*   
*   - Clears the swi7 interrupt in the hardware register.
*   - Sets a bit in the nmi source flag.
*   - Generates a software level 6 interrupt.
*
*END*----------------------------------------------------------------------*/
interrupt void swi7_nmi_handler(void)
{ 
  
  asm {
    
    // Clear swi7 interrupt.
    move.l  #0xFFFFFFDF, a0
    move.b  #0x38, (a0)           // INTC_CFRC = 0x38
       
    // Set bit to indicate sw level 7 interrupt.
    lea     gNmiSourceFlag, a0
    bset.b  #SW7_NMI_BIT, (a0)
    
    // Force level 6 sw interrupt
    moveq   #0x39,d0
    move.b  d0, 0xFFFFFFDE        // INTC_SFRC = 0x39
    
  }
  
}



/*FUNCTION*-------------------------------------------------------------------
*
* Function Name    : _bsp_enable_card
* Returned Value   : uint_32 result
* Comments         :
*   This function sets up operation of the card.
*
*END*----------------------------------------------------------------------*/

uint_32 _bsp_enable_card
   (
      void
   )
{ /* Body */
   KERNEL_DATA_STRUCT_PTR   kernel_data;
   uint_32                  result;
   
   _GET_KERNEL_DATA(kernel_data);

   /* Set the CPU type */
   _mqx_set_cpu_type(MQX_CPU);

#if MQX_EXIT_ENABLED
   /* Set the bsp exit handler, called by _mqx_exit */
   _mqx_set_exit_handler(_bsp_exit_handler);
#endif

   /* Initialize the Fxlc95000 support functions */
   _fxlc95000_initialize_support(0);
 
   /*
   ** Initialize the interrupt handling
   */
   /* Mask all interrupts */
   _fxlc95000_int_mask_all();
   
   _int_set_vector_table(BSP_INTERRUPT_VECTOR_TABLE);

   result =  _psp_int_init(BSP_FIRST_INTERRUPT_VECTOR_USED,
      BSP_LAST_INTERRUPT_VECTOR_USED);
      
   if (result != MQX_OK) {
      return result;
   } /* Endif */

   /* Initialize the timer interrupt */
   _time_set_timer_vector(BSP_TIMER_INTERRUPT_VECTOR);
   if (_int_install_isr(BSP_TIMER_INTERRUPT_VECTOR,
      (void (_CODE_PTR_)(pointer))_bsp_timer_isr, NULL) == NULL)
   {
      return MQX_TIMER_ISR_INSTALL_FAIL;
   } /* Endif */

   
#if 0
    // Setup software interrupt level 6 as a proxy for the INT and
    // frame error irq.
    if (_int_install_isr(VectorNumber_VL6swi,
      (void (_CODE_PTR_)(pointer))_bsp_swi6_isr, NULL) == NULL)
    {
       return MQX_TIMER_ISR_INSTALL_FAIL;
    } /* Endif */
#endif
   
#if 0
    // Initialize start of digital frame interrupt.
    if (_int_install_isr(VectorNumber_Vstart_of_frame,
      (void (_CODE_PTR_)(pointer))_bsp_start_of_digital_frame_isr, NULL) == NULL)
    {
       return MQX_TIMER_ISR_INSTALL_FAIL;
    } /* Endif */

    
    // NOTE: Don't enable frame irq here.  Enable it in idle thread just before
    // going into stop mode so that it can wake us up.
    // Enable start PhiD frame interrupt.
    //_fxlc95000_sdf_unmask_int();
#endif
    
    
#if 0
    // Initialize start of digital frame error interrupt.
    if (_int_install_isr(VectorNumber_Vframe_err,
      (void (_CODE_PTR_)(pointer))_bsp_FrameError_isr, NULL) == NULL)
    {
       return MQX_TIMER_ISR_INSTALL_FAIL;
    } /* Endif */
#endif
    
    
#ifdef DEBUG_ENABLE_ILLEGAL_INSTR_ISR
   if (_int_install_isr(VectorNumber_Viinstr,
      (void (_CODE_PTR_)(pointer))_bsp_timer_isr_IllegalInstr, NULL) == NULL)
   {
      return MQX_TIMER_ISR_INSTALL_FAIL;
   } /* Endif */
#endif
   

#if (BSP_TIMER == BSP_TIMER_USE_PDB)
   {
      Rtos_timer_struct* rtostimer = rtos_timer_addr_get();
#if (BSP_TIMER == BSP_TIMER_USE_PDB)      
      rtostimer->rtos_requested_tick_period_nsec = BSP_INITIAL_TICK_PERIOD_NSEC;
#endif
      
   }
#endif
   
   // Setup the rtos timer.
   _bsp_init_rtostimer();
   
   
   
#if BSPCFG_ENABLE_CPP
   /* initialize C++ constructors */
   __cpp_init();
#endif


#if BSPCFG_ENABLE_IO_SUBSYSTEM
   /* Initialize the I/O Sub-system */
   result = _io_init();
   if (result != MQX_OK) {
      return result;
   } /* Endif */

   /* Install device drivers */
   
 

#if BSPCFG_ENABLE_I2C0
   _fxlc95xxx_i2c_polled_install("i2c0:", &_bsp_i2c0_init);
#endif
#if BSPCFG_ENABLE_II2C0
   _fxlc95xxx_i2c_int_install("ii2c0:", &_bsp_i2c0_init);
#endif

   /* Install the GPIO driver */
#if BSPCFG_ENABLE_GPIODEV   
   _io_gpio_install("gpio:");
#endif


/* install internal flash */
#if BSPCFG_ENABLE_FLASHX
   fxlc95xxx_internal_flash_install("flashx:", BSPCFG_FLASHX_SIZE);
#endif

#endif

   return MQX_OK;
   

} /* Endbody */


/*FUNCTION*-------------------------------------------------------------------
*
* Function Name    : _bsp_init_rtostimer
* Returned Value   : none
* Comments         :
*    This function is called in two cases:
*    1) When system first boots and MQX is initialized
*    2) When the FLE is changed and the timer tick needs to be re-initialized.
*
* 112711 b39107
*END*----------------------------------------------------------------------*/

void _bsp_init_rtostimer
   (
      void
   )
{
  
  KERNEL_DATA_STRUCT_PTR   kernel_data;
  Rtos_timer_struct* rtostimer = rtos_timer_addr_get(); 

  _fxlc95000_timer_mask_int();

  _GET_KERNEL_DATA(kernel_data);

  kernel_data->TIMER_HW_REFERENCE =  
  #if (BSP_TIMER == BSP_TIMER_USE_TPM)
  _fxlc95000_timer_init_int((uint_32)BSP_BUS_CLOCK_HZ / (uint_32)BSP_RTOS_TICKS_PER_SEC, FALSE);
  #elif (BSP_TIMER == BSP_TIMER_USE_PDB)
  _fxlc95000_timer_init_int(FALSE);
  #endif

  _time_set_hwtick_function(_bsp_get_hwticks, (pointer)kernel_data->TIMER_HW_REFERENCE);
  _time_set_hwticks_per_tick(kernel_data->TIMER_HW_REFERENCE);

#if (BSP_TIMER == BSP_TIMER_USE_TPM)
   _time_set_ticks_per_sec((uint_32)BSP_RTOS_TICKS_PER_SEC); // ti_sett.c, sets kernel_data->TICKS_PER_SECOND
#else
   _time_set_ticks_per_sec((uint_32)rtostimer->rtos_ticks_per_sec); // ti_sett.c, sets kernel_data->TICKS_PER_SECOND
#endif
  
  _fxlc95000_timer_unmask_int();
}


/*FUNCTION*-------------------------------------------------------------------
*
* Function Name    : _bsp_exit_handler
* Returned Value   : none
* Comments         :
*    This function is called when MQX exits
*
*END*----------------------------------------------------------------------*/

void _bsp_exit_handler
   (
      void
   )
{ /* Body */
   uint_16   temp;

   temp = _psp_set_sr(0x2700);
   
   _fxlc95000_int_mask_all();

} /* Endbody */

#ifdef DEBUG_ENABLE_ILLEGAL_INSTR_ISR
void _bsp_timer_isr_IllegalInstr
   (
      pointer dummy
   )
{
  // Debug code.
}
#endif



/*ISR*********************************************************************
*
* Function Name    : _bsp_FrameError_isr
* Returned Value   : void
* Comments         :
*    Interrupt handler for frame error (digital frame overrun).
*    
*    NOTE: Frameerror irq will get triggered at level 7 (non-maskable).
*
*END**********************************************************************/
void _bsp_FrameError_isr
(
   pointer dummy
)
{ /* Body */
  _fxl95000_SetFrameOverrun_Status(TRUE);
  _fxlc95000_frameerror_clear_int();
  
#ifdef DEBUG_ENABLE_FRAMEERROR_GPIO_TOGGLE
  _fxl95000_DebugToggle_FrameError();
#endif
  
}

/*ISR*********************************************************************
*
* Function Name    : _bsp_start_of_digital_frame_isr
* Returned Value   : void
* Comments         :
*    Interrupt handler for start of digital frame event.
*
*END**********************************************************************/
#if 0
void _bsp_start_of_digital_frame_isr
   (
      pointer dummy
   )
{ /* Body */

  // Clear SF
  _fxlc95000_sdf_clear_int();

  
#ifdef DEBUG_ENABLE_STARTOFFRAME_GPIO_TOGGLE
  {
    static volatile uint_8 toggle = 0;
    
    toggle ^= 0x01;
    
    _fxl95000_DebugToggle_StartDigFrame(toggle);    
  }
#endif
  
#ifdef DEBUG_ENABLE_FRAMECOUNTER  
    ++bspframe_counter32;
#endif
}
#endif


/*ISR*********************************************************************
*
* Function Name    : _bsp_timer_isr
* Returned Value   : void
* Comments         :
*    The timer ISR is the interrupt handler for the clock tick.
*
*END**********************************************************************/


void _bsp_timer_isr
   (
      pointer dummy
   )
{ /* Body */
 
#if WATCHDOG_INITIALIZATION != WATCHDOG_DISABLED
   // Service the watchdog timer to verify we are still in control
   // TODO - Not supported yet
#endif


  _fxlc95000_timer_clear_int();

  
  /* apply sw prescaller - do not react on every timer isr */
#if (BSP_TIMER == BSP_TIMER_USE_TPM)
  if(_bsp_timer_sw_prescaller_check() == TRUE)
#endif
  {
    ++bsptimer_counter32;
    
    _time_notify_kernel();
  }
    
} /* Endbody */





/*FUNCTION*-------------------------------------------------------------------
*
* Function Name    : _bsp_get_ticktime
* Input parameters :
*     none
* Returned Value   : uint_32, tick time in nanosec
* Comments         :
* 
*
*END*----------------------------------------------------------------------*/
#if (BSP_TIMER == BSP_TIMER_USE_PDB)
uint_32 _bsp_get_ticktime(void)
{
  Rtos_timer_struct* rtostimer = rtos_timer_addr_get(); 
  
  return (uint_32)(rtostimer->rtos_tick_period_10nsec * 10);
}
#endif

/*FUNCTION*-------------------------------------------------------------------
*
* Function Name    : _bsp_change_ticktime
* Input parameters :
*     tickperiod_nsec - tick time in nanosec
* Returned Value   : uint_32, actual tick time in nano sec, zero if error.
* Comments         :
*    Changes the rtos tick time.  Returns the actual tick time set.
*
*END*----------------------------------------------------------------------*/

#if (BSP_TIMER == BSP_TIMER_USE_PDB)
uint_32 _bsp_change_ticktime(
    volatile uint_32 tickperiod_nsec 
    )
{
  Rtos_timer_struct* rtostimer = rtos_timer_addr_get(); 
  

  if ( (rtostimer->rtos_requested_tick_period_nsec >= BSP_MINIMUM_TICK_PERIOD_NSEC) || 
      (rtostimer->rtos_requested_tick_period_nsec <= BSP_MAXIMUM_TICK_PERIOD_NSEC) )
  {
    rtostimer->rtos_requested_tick_period_nsec = tickperiod_nsec; 
    // Make the change go into effect.
    _bsp_init_rtostimer();
  }
   
  
  return rtostimer->rtos_actual_tick_period_nsec;
}
#endif






/*FUNCTION*-------------------------------------------------------------------
*
* Function Name    : _bsp_get_hwticks
* Returned Value   : uint_32
* Comments         :
*    This function returns the number of hw ticks that have elapsed
* since the last interrupt
*
*END*----------------------------------------------------------------------*/


static uint_32 _bsp_get_hwticks
   (
      pointer param
   )
{ /* Body */
   
   return _fxlc95000_get_hwticks(); 

} /* Endbody */



/*FUNCTION*-------------------------------------------------------------------
*
* Function Name    : _bsp_setup_watchdog
* Returned Value   : uint_32
* Comments         :
*    This function initializes the watchdog timer according to the definition
* WATCHDOG_INITIALIZATION in <BOARD_NAME>.h
*
*END*----------------------------------------------------------------------*/

static void _bsp_setup_watchdog
   (
      void
   )
{ /* Body */
// TODO Not supported yet
} /* Endbody */




/* EOF */
