/******************************************************************************
            TEXAS INSTRUMENTS INCORPORATED PROPRIETARY INFORMATION           
                                                                             
   Property of Texas Instruments -- For  Unrestricted  Internal  Use  Only 
   Unauthorized reproduction and/or distribution is strictly prohibited.  This 
   product  is  protected  under  copyright  law  and  trade  secret law as an 
   unpublished work.  Created 1987, (C) Copyright 1997 Texas Instruments.  All 
   rights reserved.                                                            
                  
                                                           
   Filename       	: clkm.c

   Description    	: Set of functions useful to test the Saturn
			  CLKM peripheral 

   Project        	: drivers

   Author         	: pmonteil@tif.ti.com  Patrice Monteil.

   Version number   : 1.13

   Date             : 05/23/03

   Previous delta 	: 10/23/01 14:43:31

   Sccs Id  (SID)       : '@(#) clkm.c 1.11 10/23/01 14:43:31 '

*****************************************************************************/

           //############################################################
           //############################################################
           //### Be careful: this file must be placed in Flash Memory ###
           //###     and compiled in 16 bits length intructions       ###
           //###        (CF. the function wait_ARM_cycles()           ###
           //############################################################
           //############################################################
           
#include "l1sw.cfg"
#include "chipset.cfg"
#include "board.cfg"
#include "swconfig.cfg"

#if (OP_L1_STANDALONE == 0)
  #include "main/sys_types.h"
#else
  #include "sys_types.h"
#endif

#include "clkm.h" 


#if (CHIPSET == 12)
    #include "sys_memif.h"
#else
    #include "memif/mem.h"
#endif

#if (BOARD == 34)
  #include "armio/armio.h"
  #include "timer/timer.h"
#endif

static SYS_UWORD32 ratio_wait_loop = 0;

#if (CHIPSET == 12)
  const double dsp_div_value[CLKM_NB_DSP_DIV_VALUE] = {1, 1.5, 2, 3};
#endif

#if (BOARD == 34)
/*
 *  CLKM_InitARMClock()
 *
 * This init is for VTCX0 = 13 MHz
 *   (use CLKM_VTCXO_26 if VTCX0 is 26 MHz)
 * Parameters :    src : 0x0 means EXT CLK (mpu dpll) selected
 *                       0x1 means VTCX0 selected
 *                 div : Division factor applied to clock
 *                         source
 *                        (div = 3 -> divise by 3/2 in fact)
 *             WARNING : reverse order in comparison to ULYSSE
 *
 * Return     : none
 * Functionality :Initialize the ARM Clock frequency
 */

void CLKM_InitARMClock(int src, int div)
{
    SYS_UWORD16 cntl = * (volatile SYS_UWORD16 *) CLKM_ARM_CLK;
    int clk_xp5, clk_div;

    if (div == 3)
    clk_xp5 = 1;
    else
    clk_xp5 = 0;

    if (div == 2)
    clk_div = 1;
    else if (div == 4) 
    clk_div = 0;
    else
    clk_div = 3;

    cntl &= ~(MASK_ARM_MCLK_1P5 | CLKM_MCLK_DIV);
    cntl |= ((clk_xp5 << 3) | (clk_div << 4));

    * (volatile SYS_UWORD16 *) CLKM_ARM_CLK = cntl;
    if (src)
    CLKM_EnableDPLL(0);
    else
    CLKM_EnableDPLL(1);
}

/*
 * CLKM_SetMclkDiv
 *
 * Set divider 
 *
 * Parameter : 2-bit divider as per spec (0-7)
 * 
 * Side-effect : compute magic delay for busy loops
 *
 */
void CLKM_SetMclkDiv(int div)
{
    volatile SYS_UWORD16 clkm_ctrl;
    clkm_ctrl = *((volatile SYS_UWORD16 *) CLKM_ARM_CLK); // read register
    clkm_ctrl &= ~CLKM_MCLK_DIV;	
    clkm_ctrl |= (div << 4);
    *((volatile SYS_UWORD16 *) CLKM_ARM_CLK) = clkm_ctrl;
}

/*
 * CLKM_EnableDPLL
 *
 * Enable or disable 48mhz PLL for ARM clock
 *
 * Parameter : 1 or 0
 *
 * Side-effect : compute magic delay for busy loops
 *
 */
void CLKM_EnableDPLL(int enable)
{
    volatile SYS_UWORD16 clkm_ctrl;

    // read CLKM register
    clkm_ctrl = *((volatile SYS_UWORD16 *) CLKM_ARM_CLK);

    if (enable) 
    {
        // PARAMETERS tuned for the AVENGER 2 reference design
        // we wait before accessing external memory at wake up
        // we have 2.5 ms margin before the first IT TDMA, we wait
        //
        //  5000     loop cycles
        //  5000 * 5 arm7 cycles
        // giving <= 1 ms at 26 MHz
        //
        wait_ARM_cycles(5000);
    }
    else 
    {
        // reset bit for VTCXO
        clkm_ctrl &= ~CLKM_CLKIN_SEL;
        *((volatile SYS_UWORD16 *) CLKM_ARM_CLK) = clkm_ctrl;

        // disable clk48mhz
        AI_ResetBit(6);
    }
}

/*
 * CLKM_EnableSharedMemClock
 *
 * Enable or disable shared mem clock 
 *
 * Parameter : 1 or 0
 *
 */
void CLKM_EnableSharedMemClock(int enable)
{
    if (enable) 
    {
        // request shared mem clock and wait for MPU HW acknowledge
        AI_ResetBit(4);
        while(AI_ReadBit(5)!=1); 
    }
    else 
    {
        // disable shared mem clock
        AI_SetBit(4);
    }
}

/*
 * CLKM_InitLeadClock
 * 
 * Parameter : onoff, mul, ndiv, div 
 *  
 * onoff -> (1:pll on) (0: pll off)
 * if div = 0  -> x(plmul+1) 
 * if div = 1  -> x(plmul+1)/2 if plmul is even
 *                x(plmul/4)   if plmul is odd
 * ndiv
 */

void CLKM_InitLeadClock(int onoff, int mul, int ndiv, int div)
{
    int pldiv, pllndiv ;
    SYS_UWORD16 value = 0;

    value |=  onoff & CLKM_PLONOFF ;
    value |= (mul << 1) & CLKM_PLMUL;
    value |= (ndiv << 5)& CLKM_PLLNDIV;
    value |= (div << 6) & CLKM_PLDIV;

    CLKM_INITLEADPLL(value);
}

#elif ((CHIPSET == 4) || (CHIPSET == 7) || (CHIPSET == 8) || (CHIPSET == 10) || (CHIPSET == 11) || (CHIPSET == 12))
  /*--------------------------------------------------------------*/
  /*  CLKM_InitARMClock()                                         */
  /*--------------------------------------------------------------*/
  /* Parameters :  clk_src : 0x00 means DPLL selected             */
  /*                         0x01 means VTCX0 selected            */
  /*                         0x03 means CLKIN selected            */
  /*               clk_xp5 : Enable 1.5 or 2.5 division factor    */
  /*                         (0 or 1)                             */
  /*               clk_div : Division factor applied to clock     */
  /*                         source                               */
  /*             WARNING : reverse order in comparison to ULYSSE  */
  /*                                                              */
  /* Return     : none                                            */
  /* Functionality :Initialize the ARM Clock frequency            */
  /*--------------------------------------------------------------*/
  void CLKM_InitARMClock(SYS_UWORD16 clk_src, SYS_UWORD16 clk_div, SYS_UWORD16 clk_xp5)
  {
    SYS_UWORD16 cntl = * (volatile SYS_UWORD16 *) CLKM_ARM_CLK;
        
    cntl &= ~(CLKM_CLKIN0 | CLKM_CLKIN_SEL | CLKM_ARM_MCLK_XP5 | CLKM_MCLK_DIV);
    
    cntl |= ((clk_src << 1) | (clk_xp5 << 3) | (clk_div << 4));
    
    * (volatile SYS_UWORD16 *) CLKM_ARM_CLK = cntl;
  }
#else
  /*--------------------------------------------------------------
   *  CLKM_InitARMClock()
   *--------------------------------------------------------------
   * Parameters :  clk_src : 0x00 means CLKIN selected
   *	  	  	   0x01 means 32 K selected
   *	 	  	   0x02 means External clock selected
   *
   * Return     :	none
   * Functionality :Initialize the ARM Clock frequency
   *--------------------------------------------------------------*/
  void CLKM_InitARMClock(SYS_UWORD16 clk_src, SYS_UWORD16 clk_div)
  {
    SYS_UWORD16 cntl = * (volatile SYS_UWORD16 *) CLKM_ARM_CLK;

    cntl &= ~(CLKM_LOW_FRQ | CLKM_CLKIN_SEL | CLKM_MCLK_DIV);

    cntl |= ((clk_src << 1) | (clk_div << 4));

    * (volatile SYS_UWORD16 *) CLKM_ARM_CLK = cntl;
  }

#endif


/*-------------------------------------------------------*/ 
/* convert_nanosec_to_cycles()                           */
/*-------------------------------------------------------*/
/* parameter: time in 10E-9 seconds                      */
/* return: Number of cycles for the wait_ARM_cycles()    */
/*         function                                      */
/*                                                       */
/* Description:                                          */
/* ------------                                          */
/* convert x nanoseconds in y cycles used by the ASM loop*/
/* function . Before calling this function, call the     */ 
/* initialize_wait_loop() function                       */
/* Called when the HardWare needs time to wait           */
/*-------------------------------------------------------*/ 
SYS_UWORD32 convert_nanosec_to_cycles(SYS_UWORD32 time)
{ 
  return( time / ratio_wait_loop);  
}


/*-------------------------------------------------------*/
/* initialize_wait_loop()                                */
/*-------------------------------------------------------*/
/*                                                       */
/* Description:                                          */
/* ------------                                          */
/* Init the ratio used to convert time->Cycles according */
/* to hardware parameters                                */
/* measurement time for this function (ARM 39Mhz, 3 waits*/
/* states) = 75 micoseconds                              */
/*-------------------------------------------------------*/

void initialize_wait_loop(void)
{
#if (BOARD == 34)
    unsigned long ulTimeSpent=0;

    // set up timer 2 for wait_ARM_cycles function calibration
    TM_EnableTimer (2);
    TM_ResetTimer (2, 0xFFFF, 0, 0);

    // run wait_ARM_cycles() for 10000 loops
    wait_ARM_cycles(10000);

    // time spent expressed in timer cycles
    // where 1 timer cycle = 2462 ns with prescale 0
    // 13 MHz divided by 16 = timer clkin 
    // prescale 0 -> divided by 2
    ulTimeSpent = TM_ReadTimer (2);

    TM_StopTimer (2);

    ulTimeSpent = 0xFFFF - ulTimeSpent;
    ulTimeSpent *= 2462;

    // compute ratio_wait_loop
    ratio_wait_loop = (unsigned long)(ulTimeSpent/10000.);
#else
  #define NBR_CYCLES_IN_LOOP   5   // this value is got from an oscilloscope measurement
  
  double src_ratio;
  double final_ratio;

  SYS_UWORD16 flash_access_size;
  SYS_UWORD16 flash_wait_state;
  SYS_UWORD32 nbr;
  SYS_UWORD32 arm_clock;

  //////////////////////////////////
  //  compute the ARM clock used  //
  //////////////////////////////////
  {
    SYS_UWORD16 arm_mclk_xp5;
    SYS_UWORD16 arm_ratio;
    SYS_UWORD16 clk_src;
    SYS_UWORD16 clkm_cntl_arm_clk_reg = * (volatile SYS_UWORD16 *) CLKM_CNTL_ARM_CLK;

    #if ((CHIPSET == 4) || (CHIPSET == 7) || (CHIPSET == 8) || (CHIPSET == 10) || (CHIPSET == 11) || (CHIPSET == 12))
      clk_src  = (clkm_cntl_arm_clk_reg & MASK_CLKIN) >> 1;
      switch (clk_src)
      {
        case 0x00: //DPLL selected 
          // select the DPLL factor
           #if (CHIPSET == 12)
              if (((* (volatile SYS_UWORD16 *) C_MAP_DPLL_BASE) & DPLL_LOCK) != 0)
           #else
              if (((* (volatile SYS_UWORD16 *) MEM_DPLL_ADDR) & DPLL_LOCK) != 0)
           #endif 
          {
             SYS_UWORD16 dpll_div;
             SYS_UWORD16 dpll_mul;

             dpll_div=DPLL_READ_DPLL_DIV;
             dpll_mul=DPLL_READ_DPLL_MUL;
             src_ratio = (double)(dpll_mul)/(double)(dpll_div+1);              
          }
          else // DPLL in bypass mode
          {
             SYS_UWORD16 dpll_div = DPLL_BYPASS_DIV;
             src_ratio= (double)(1)/(double)(dpll_div+1);
          }
          break;
        case 0x01: //VTCX0 selected 
          src_ratio = 1;
          break;
        case 0x03: //CLKIN selected   (external clock)
          src_ratio = 1;
          break;
      }
      // define the division factor applied to clock source (CLKIN or VTCXO or DPLL)
      arm_ratio = (clkm_cntl_arm_clk_reg & CLKM_MCLK_DIV) >> 4;

      // check if the 1.5 or 2.5 division factor is enabled
      arm_mclk_xp5  = clkm_cntl_arm_clk_reg & CLKM_ARM_MCLK_XP5;

      if (arm_mclk_xp5 == 0) // division factor enable for ARM clock ?
      {
        if (arm_ratio == 0) 
          arm_ratio =1; 
      }
      else
        arm_ratio = ((arm_ratio>>1) & 0x0001) == 0 ? 1.5 : 2.5;


     #else
      src_ratio = 1;

      // define the division factor applied to clock source (CLKIN or VTCXO or DPLL)
      arm_ratio = (clkm_cntl_arm_clk_reg & CLKM_MCLK_DIV) >> 4;

      // check if the 1.5 or 2.5 division factor is enabled
      arm_mclk_xp5  = clkm_cntl_arm_clk_reg & MASK_ARM_MCLK_1P5;

      if (arm_mclk_xp5 == 1) // division factor enable for ARM clock ?
        arm_ratio = 1.5;  
      else
      {
        if (arm_ratio == 0)
          arm_ratio = 4;
        else 
          if (arm_ratio == 1 )
            arm_ratio = 2;
          else 
            arm_ratio = 1;
      }

     #endif

   final_ratio = (src_ratio / (double) arm_ratio);

  }
  //////////////////////////////////////////
  //  compute the Flash wait states used  //
  //////////////////////////////////////////

  #if (CHIPSET == 12)
     flash_access_size  =  1;
  #else
    flash_access_size  =  *((volatile SYS_UWORD16 *) MEM_REG_nCS0);
  #endif
  flash_access_size  = (flash_access_size >> 5) & 0x0003; // 0=>8bits, 1=>16 bits, 2 =>32 bits

  // the loop file is compiled in 16 bits it means
  //    flash 8  bits => 2 loads for 1 16 bits assembler instruction
  //    flash 16 bits => 1 loads for 1 16 bits assembler instruction
  //    flash/internal RAM 32 bits => 1 loads for 1 16 bits assembler instruction (ARM bus 16 bits !!)
  
  // !!!!!!!!! be careful: if this file is compile in 32 bits, change these 2 lines here after !!!
  if (flash_access_size == 0) flash_access_size = 2;
  else                        flash_access_size = 1;

  #if (CHIPSET == 12)
    /*
     *  loop move to run in internal memory, due to page mode in external memory
     */
    flash_wait_state  =  0;
  #else
    flash_wait_state  =  *((volatile SYS_UWORD16 *) MEM_REG_nCS0);
    flash_wait_state &=  0x001F;
  #endif

  //////////////////////////////////////
  //  compute the length of the loop  //
  //////////////////////////////////////

  // Number of flash cycles for the assembler loop
  nbr = NBR_CYCLES_IN_LOOP;

  // Number of ARM cycles for the assembler loop
  nbr = nbr * (flash_wait_state + 1) * (flash_access_size);

  // time for the assembler loop (unit nanoseconds: 10E-9)
  arm_clock = final_ratio * 13; // ARM clock in Mhz 
  ratio_wait_loop = (SYS_UWORD32)((nbr*1000) / arm_clock);
#endif
}

#if (CHIPSET != 12)

/*-------------------------------------------------------*/ 
/* wait_ARM_cycles()                                     */
/*-------------------------------------------------------*/
/*                                                       */
/* Description:                                          */
/* ------------                                          */
/* Called when the HardWare needs time to wait.          */
/* this function wait x cycles and is used with the      */
/* convert_nanosec_to_cycles() & initialize_wait_loop()  */
/*                                                       */
/*  Exemple:  wait 10 micro seconds:                     */
/*  initialize_wait_loop();                              */
/*  wait_ARM_cycles(convert_nanosec_to_cycles(10000))    */
/*                                                       */
/*  minimum time value with cpt_loop = 0  (estimated)    */
/*  and C-SAMPLE / flash 6,5Mhz  ~  1,5 micro seconds    */
/*                                                       */
/*                                                       */
/* Be careful : in order to respect the rule about the   */
/* conversion "time => number of cylcles in this loop"   */
/* (Cf the functions: convert_nanosec_to_cycles() and    */
/* initialize_wait_loop() ) respect the following rules: */
/* This function must be placed in Flash Memory and      */
/* compiled in 16 bits instructions length               */
/*-------------------------------------------------------*/
void wait_ARM_cycles(SYS_UWORD32 cpt_loop) 
{
  // C code:
  // while (cpt_loop -- != 0);

  asm(" CMP       A1, #0");                 
  asm(" BEQ       END_FUNCTION");           

  asm("LOOP_LINE:        ");                
  asm(" SUB       A1, A1, #1");
  asm(" CMP       A1, #0");
  asm(" BNE       LOOP_LINE");

  asm("END_FUNCTION:        ");  
} 

#endif /* (CHIPSET != 12)*/
