/**HEADER********************************************************************
* 
* Copyright (c) 2008 Freescale Semiconductor;
* All Rights Reserved
*
* Copyright (c) 2004-2008 Embedded Access Inc.;
* All Rights Reserved
*
* Copyright (c) 1989-2008 ARC International;
* 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: timer_fxlc95000.c$
* $Version : 1.0.0.0$
* $Date    : Jun-9-2010$
*
* Comments:
*
*   This file contains timer functions for use with a FXLC95000.
*
*END************************************************************************/

#include "mqx.h"
#include "bsp.h"

#include "mqx_prv.h"      // _INT_DISABLE
#include "fxlc95000.h"
#include "timer_fxlc95000.h"




#if (BSP_TIMER == BSP_TIMER_USE_TPM)
/* function and varibales needed for sw prescaller */
boolean _bsp_timer_sw_prescaller_check();
static int_16 _bsp_timer_sw_prescaller;
static int_16 _bsp_timer_sw_prescaller_cnt;

uint_16 gRtos_ticks_per_sec =  BSP_RTOS_TICKS_PER_SEC;
#elif (BSP_TIMER == BSP_TIMER_USE_PDB)
//volatile uint_8 gRtos_current_FLE =  BSP_INITIAL_FLE;
Rtos_timer_struct fxlc95000_rtos_timer;
#endif


static volatile uint_32 _bsp_timer_hw_prescaller = 0;


#if (BSP_TIMER == BSP_TIMER_USE_PDB)

  void _fxlc95000_timer_mask_int(void) 
  { 
    PDB_SCR &= ~PDB_SCR_IENA_MASK;  
    //FCSR &= ~FCSR_SFDIE_MASK; 
  }
  
  void _fxlc95000_timer_unmask_int(void) 
  { 
    PDB_SCR |= PDB_SCR_IENA_MASK; 
    //FCSR |= FCSR_SFDIE_MASK; 
  }
  
  void _fxlc95000_timer_clear_int(void) 
  { 
    PDB_SCR |= 0x0800; 
    //FCSR &= ~FCSR_SF_MASK; 
  }

#endif


/*FUNCTION*-----------------------------------------------------------------
*
* Function Name   : _bsp_timer_sw_prescaller
* Returned Value  : TRUE if functions was called _bsp_timer_sw_prescaller-times 
* Comments        :
*     Sw prescaller funciton
*
*END*---------------------------------------------------------------------*/
#if (BSP_TIMER == BSP_TIMER_USE_TPM)
boolean _bsp_timer_sw_prescaller_check() {
  
  _bsp_timer_sw_prescaller_cnt++;
  
  if( _bsp_timer_sw_prescaller_cnt >= _bsp_timer_sw_prescaller) {
      _bsp_timer_sw_prescaller_cnt = 0;
      return TRUE;
  } else {
      return FALSE;
  }
}
#endif

/*FUNCTION*-----------------------------------------------------------------
*
* Function Name   : _fxlc95000_timer_init_int
* Returned Value  : the clock rate for the timer (ns per hw tick)
* Comments        :
*   this function will set up a timer to interrupt
*
*END*---------------------------------------------------------------------*/

uint_32 _fxlc95000_timer_init_int
   (
#if (BSP_TIMER == BSP_TIMER_USE_TPM)
      /* [IN] clk cycles per rtos tick */
      uint_32   clocks_per_rtos_tick,   // Also known as ticks per rtos tick
//#elif (BSP_TIMER == BSP_TIMER_USE_PDB)
//      /* [IN] rtos ticks per frame */
//      uint_32   rtos_ticks_per_frame,
#endif
      /* [IN] Unmask the timer after initializing */
      boolean   unmask_timer
   )
{ /* Body */


  int_32 rate, prescale, fle;

     
#if (BSP_TIMER == BSP_TIMER_USE_TPM)

    volatile int_32 tmpclocks_per_rtos_tick;

     _bsp_timer_sw_prescaller = 0;
     
     /* compute rate, sw prescaller and precaller */
     do{
        prescale = -1;
        _bsp_timer_sw_prescaller++;
        tmpclocks_per_rtos_tick = clocks_per_rtos_tick / _bsp_timer_sw_prescaller;
        /* compute the rate & prescaller*/
        do {
           prescale++;
           rate = tmpclocks_per_rtos_tick >> prescale;  
        } while ((rate > 0x10000) && (prescale < 8));
     } while((rate > 0xFFFF) || (prescale > 7)); /* rate and prescaller are out of range, increment sw_prescaller */

     _bsp_timer_hw_prescaller = prescale; 
     _bsp_timer_sw_prescaller_cnt = 0;
     
     //configure TPM module
     // NOTE: Disable tpm before programming modulo reg.
     TPMSC   = 0x00;

     /* set registers */ 
     // NOTE: Whether you are using the main TPM modulo, ch0, or ch1 for interrupt, you
     // must always set the modulo count to the calculated rate.  This will give correct
     // timing if you are using ch0 or ch1.
     TPMMOD = (uint_16) (rate-1);        

     // Now let tpm run. 
     TPMSC |= ((uint_8) prescale)|TPMxSC_CLK_BUSCLK; 
     
     TPMSC |= (unmask_timer ? (TPMSC_TOIE_MASK) : 0);
        
     TPMCNT = (uint_16)0xffff; /* reset the counter */

#elif (BSP_TIMER == BSP_TIMER_USE_PDB)
     
     uint_32 clocks_per_rtos_tick;   // Also known as ticks per rtos tick
     Rtos_timer_struct* rtostimer = rtos_timer_addr_get(); 
     
     rtostimer->rtos_tick_period_10nsec = rtostimer->rtos_requested_tick_period_nsec / 10;
          
     
     // Get clks per rtos tick (ticks per rtos tick) to calculate modulo.
     // NOTE: rtos_tick_period_10nsec is a 32bit var.  Max value that it could have is (2^32/100)
     // according to equation below so that it does not overflow.  This works out to be 429msec @16Mhz.
     clocks_per_rtos_tick = (uint_32)((rtostimer->rtos_tick_period_10nsec * 100) / (uint_32)(BSP_BUS_CLOCK_PERIOD_100PICOSEC));

     // Update ticks per sec
     rtostimer->rtos_ticks_per_sec = (1000 * 1000 * 100) / rtostimer->rtos_tick_period_10nsec;
     rtostimer->rtos_actual_tick_period_nsec = rtostimer->rtos_tick_period_10nsec * 10;
     
     
     /* compute the rate & prescaller*/
     prescale = -1;
     do {
        prescale++;
        rate = clocks_per_rtos_tick >> prescale;  
     } while ((rate > 0xFFFF) && (prescale < 8));

     
     _bsp_timer_hw_prescaller = prescale; 
     
     PDB_SCR = 0;  // Disable module before programming
     
     // Modulo comes into play only for continuous mode.  It sets the delay in between
     // the same pulse.  
     // - If in cont mode, delay for A/B must be non-zero in order to see cont pulses.
     // - If in cont mode and delay for A/B is zero, then NO pulse is generated.
     // - If mod is zero and cont mode, then A/B pulse is same as one-shot mode.
     PDB_MOD = rate-1;

     PDB_SCR = 
       (prescale << 13)  | // Prescaller, 3 bits
       //(0x01 << 10)  | // B irq enable, 0-disable, 1-enable
       (0x01 << 9)   | // A irq enable, 0-disable, 1-enable
       //(0x01 << 7)   | // B out select: 0-zero, 1-B=B*, 2-Pulse out, 3-reserved
       (0x01 << 5)   | // A out select: 0-zero, 1-A=A*, 2-Pulse out, 3-reserved
       (0x01 << 4)   | // 0-single shot, 1-cont
       (0x00 << 1);    // Input trigger 0-sw trig, 1- analog start, 2-dig start, 3-reserved
     
     
     // Delay setting sets the delay of pulse generation from trigger input.
     // When FLE value is 7-4, you must set at least one of the A/B delay to 1 or
     // higher to get PDB interrupt.  The channel, either A or B, that has delay
     // greater than zero will generate a pulse.
     PDB_DELAYA = 0x01;    // RGPIO5/PDB_A/INT_O
     //PDB_DELAYB = 0x01;    // RGPIO8/PDB_B
     
     // Enable pdb timer and generate sw trigger.  Must break this into two step, enable then trigger.
     PDB_SCR |= PDB_SCR_EN_MASK;
     PDB_SCR |= PDB_SCR_SWTRIG_MASK;
          
#else
     
  #error ERROR: Undefined timer for rtos tick!
     
#endif
     
     
  /* return number of hw-ticks per MQX tick */
#if (BSP_TIMER == BSP_TIMER_USE_TPM)
   return rate * _bsp_timer_sw_prescaller;
#elif (BSP_TIMER == BSP_TIMER_USE_PDB) 
   return rate;
#endif
   
} /* Endbody */




/*FUNCTION*-----------------------------------------------------------------
*
* Function Name   : _fxlc95000_get_hwticks
* Returned Value  : Number of ticks
* Comments        :
*   This function will return the number of ticks elapsed since the last
* timer tick.
*
*END*---------------------------------------------------------------------*/

uint_32 _fxlc95000_get_hwticks
   ( void )
{ /* Body */

  uint_32 ticks;   

  
#if (BSP_TIMER == BSP_TIMER_USE_TPM)

  ticks = (uint_16)TPMCNT;   
 
  if ((TPMSC & TPMxSC_TOF_MASK) != 0) {
    // Another full TICK period has expired since we handled the last timer interrupt.  
    // We need to read the counter (PCNTR) again, since the wrap may have occurred 
    // between the previous read and the checking of the PIF bit.
    ticks = (uint_16)TPMMOD + (uint_16)TPMCNT;
  } /* Endif */

  /* actual ticks in TPMCNT + what we have missed because of sw prescaller */ 
  return ticks +  (TPMMOD * _bsp_timer_sw_prescaller_cnt);

#elif (BSP_TIMER == BSP_TIMER_USE_PDB)

  ticks = (uint_16)PDB_COUNT;   

  if ((PDB_SCR & PDB_SCR_SA_MASK) != 0) {
    // Another full TICK period has expired since we handled the last timer interrupt.  
    // We need to read the counter again, since the wrap may have occurred 
    // between the previous read and the checking of the StickyA bit.
    ticks = (uint_16)PDB_MOD + (uint_16)PDB_COUNT;
  } /* Endif */
  
  return ticks;

#else
     
  #error ERROR: Undefined timer for rtos tick!
     
#endif
  
  
} /* Endbody */


/*FUNCTION*-----------------------------------------------------------------
*
* Function Name   : _fxlc95000_timer_get_nsec
* Returned Value  : nanoseconds
* Comments        :
*   this function will return the number of nanoseconds elapsed since
* the last timer tick.
*
*END*---------------------------------------------------------------------*/

uint_32 _fxlc95000_timer_get_nsec
   (
      /* [IN] Bus clock in picosec per clk tick */
      uint_32   picosec_per_clk_tick
   )
{ /* Body */

  uint_32 ticks;
  uint_32 picosec_per_tick;
  
  uint_32 usec;
  uint_32 timercounter;
  
  // Bug fix: Must disable interrupt while getting the register values.
  _INT_DISABLE();
  ticks = _fxlc95000_get_hwticks();
  timercounter = bsptimer_counter32;
  _INT_ENABLE();

  

  // NOTE: This code has less latency than the one below, but has less accuracy.
  // picosec_per_tick = 62500 psec or 62.5 nsec @ 16Mhz.  Dividing by 1000 would result in 62 psec after rounding.
  //return  (  (bsptimer_counter32 * BSP_INITIAL_TICK_PERIOD_NSEC) + (picosec_per_tick/1000 * ticks)); // Divide by 1000 to get nanosec  

  // NOTE: This requires some operations that cost many cycles.
  //picosec_per_tick = GET_PICOSEC_PER_TICK(picosec_per_clk_tick);
  // Instead, we save prescaler in global and shift it.  This saves time.
  picosec_per_tick = picosec_per_clk_tick << _bsp_timer_hw_prescaller;
  usec =  (timercounter * BSP_INITIAL_TICK_PERIOD_NSEC) + ((picosec_per_tick * ticks)/1000); // Divide by 1000 to get nanosec  
  
  return usec;
} /* Endbody */




/*FUNCTION*-----------------------------------------------------------------
*
* Function Name   : _fxlc95000_timer_get_usec
* Returned Value  : microseconds
* Comments        :
*   this function will return the number of microseconds elapsed since
* the start of system run.
*
*END*---------------------------------------------------------------------*/

uint_32 _fxlc95000_timer_get_usec
   (
      /* [IN] Bus clock in picosec per clk tick */
      uint_32   picosec_per_clk_tick
   )
{ /* Body */

  uint_32 ticks;
  uint_32 picosec_per_tick;

  uint_32 usec;
  uint_32 timercounter;
  
  // Bug fix: Must disable interrupt while getting the register values.
  _INT_DISABLE();
  ticks = _fxlc95000_get_hwticks();
  timercounter = bsptimer_counter32;
  _INT_ENABLE();

  // NOTE: This requires some operations that cost many cycles.
  //picosec_per_tick = GET_PICOSEC_PER_TICK(picosec_per_clk_tick);
  // Instead, we save prescaler in global and shift it.  This saves time.
  picosec_per_tick = picosec_per_clk_tick << _bsp_timer_hw_prescaller;
  usec =  (timercounter * (BSP_INITIAL_TICK_PERIOD_NSEC/1000)) + ((picosec_per_tick * ticks)/(1000*1000));
  
  return usec;

} /* Endbody */



 
/* EOF */
