view src/cs/drivers/drv_core/abb/abb.c @ 220:0ed36de51973

ABB semaphore protection overhaul The ABB semaphone protection logic that came with TCS211 from TI was broken in several ways: * Some semaphore-protected functions were called from Application_Initialize() context. NU_Obtain_Semaphore() called with NU_SUSPEND fails with NU_INVALID_SUSPEND in this context, but the return value wasn't checked, and NU_Release_Semaphore() would be called unconditionally at the end. The latter call would increment the semaphore count past 1, making the semaphore no longer binary and thus no longer effective for resource protection. The fix is to check the return value from NU_Obtain_Semaphore() and skip the NU_Release_Semaphore() call if the semaphore wasn't properly obtained. * Some SPI hardware manipulation was being done before entering the semaphore- protected critical section. The fix is to reorder the code: first obtain the semaphore, then do everything else. * In the corner case of L1/DSP recovery, l1_abb_power_on() would call some non-semaphore-protected ABB & SPI init functions. The fix is to skip those calls in the case of recovery. * A few additional corner cases existed, all of which are fixed by making ABB semaphore protection 100% consistent for all ABB functions and code paths. There is still one remaining problem of priority inversion: suppose a low- priority task calls an ABB function, and some medium-priority task just happens to preempt right in the middle of that semaphore-protected ABB operation. Then the high-priority SPI task is locked out for a non-deterministic time until that medium-priority task finishes its work and goes back to sleep. This priority inversion problem remains outstanding for now.
author Mychaela Falconia <falcon@freecalypso.org>
date Mon, 26 Apr 2021 20:55:25 +0000
parents d00662aa64d8
children 160a5b3a076c
line wrap: on
line source

/**********************************************************************************/
/*            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         : abb.c                                                     */
/*                                                                                */
/*   Description      : Functions to drive the ABB device.                        */
/*                      The Serial Port Interface is used to connect the TI       */
/*                      Analog BaseBand (ABB).                                    */
/*                      It is assumed that the ABB is connected as the SPI        */
/*                      device 0.                                                 */
/*                                                                                */
/*   Author           : Pascal PUEL                                               */
/*                                                                                */
/*   Version number   : 1.3                                                       */
/*                                                                                */
/*   Date and time    : 08/22/03                                                       */
/*                                                                                */
/*   Previous delta   : Creation                                                  */
/*                                                                                */
/**********************************************************************************/

#include "l1sw.cfg"

#include "chipset.cfg"
#include "board.cfg"
#include "rf.cfg"
#include "swconfig.cfg"
#include "sys.cfg"
#include "abb.h"
#include "l1_macro.h"
#include "l1_confg.h"
#include "clkm/clkm.h"         // for wait_ARM_cycles function
#include "abb_inline.h"
#include "ulpd/ulpd.h"        // for FRAME_STOP definition
#include "nucleus.h"      // for NUCLEUS functions and types
#include "l1_types.h"

#if (OP_L1_STANDALONE == 0)
  #include "main/sys_types.h"
  #include "rv/general.h"
  #include "buzzer/buzzer.h"	     // for BZ_KeyBeep_OFF function
#else
  #include "sys_types.h"
#endif

#if (VCXO_ALGO == 1)
  #include "l1_ctl.h"
#endif

#if (RF_FAM == 35)
  #include "l1_rf35.h"
#endif

#if (RF_FAM == 12)
  #include "tpudrv12.h"
  #include "l1_rf12.h"
#endif

#if (RF_FAM == 10)
  #include "l1_rf10.h"
#endif

#if (RF_FAM == 8)
  #include "l1_rf8.h"
#endif

#if (RF_FAM == 2)
  #include "l1_rf2.h"
#endif

/*
 * The following conditional compilation control is a FreeCalypso addition.
 * TI's original code always configured the BCICONF register with
 * MESBB and BBCHGEN bits set, enabling both charging and the measurement
 * resistive divider for the backup battery.  However, on our primary
 * hw targets (Openmoko GTA02 and our own FCDEV3B) Iota's VBACKUP pin
 * is unconnected, whereas on Mot C139 and Pirelli DP-L10 "alien" hw
 * the VBACKUP situation is unclear.  But at least on our known hw
 * with VBACKUP unconnected, it is better to leave backup battery charging
 * and measurement OFF - TI's original config seems to be a drain on
 * the main battery.  Therefore, we are going to leave MESBB and BBCHGEN
 * off until and unless we have a hw target where backup battery charging
 * and measurement are appropriate.
 */

#define	ENABLE_BACKUP_BATTERY	0

/*
 * The following ABB_sleep_allowed global variable is yet another FreeCalypso
 * addition.  Here is the issue: some handset boards have the controller/driver
 * chip in the LCD powered from Iota VRIO, which is generally a very sensible
 * arrangement.  As one reference example, our 176x220 pixel TFT LCDs which
 * we are considering for our own FC handset draw about 3 mA from their Vci
 * supply which we connect to VRIO - perfectly fine when the regulators are
 * in their normal Active mode.  But what about sleep mode?  Sleep mode VRIO
 * current limit is only 1 mA, thus the combination of the LCD being on and
 * drawing 3 mA with the ABB in sleep mode is invalid.  TI's original code
 * already had a check for VRPCSTS: PWON and RPWON need to be released and
 * the charger needs to be unplugged in order to enter ABB superdeep sleep.
 * We are extending this check with one more condition: ABB_sleep_allowed
 * needs to be nonzero; the intent is that this variable will be set by the
 * code responsible for putting the LCD into its own powerdown mode.
 * This logic is included only for affected targets with LCDs.
 */

#ifdef CONFIG_TARGET_LUNA
int ABB_sleep_allowed = 0;
#endif

#if (ABB_SEMAPHORE_PROTECTION)

static NU_SEMAPHORE abb_sem;

/*-----------------------------------------------------------------------*/
/* ABB_Sem_Create()                                                      */
/*                                                                       */
/* This function creates the Nucleus semaphore to protect ABB accesses   */
/* against preemption.                                                   */
/* No check on the result.                                               */
/*                                                                       */
/*-----------------------------------------------------------------------*/
void ABB_Sem_Create(void)
{
  // create a semaphore with an initial count of 1 and with FIFO type suspension.
  NU_Create_Semaphore(&abb_sem, "ABB_SEM", 1, NU_FIFO);
}

#endif  // ABB_SEMAPHORE_PROTECTION

/*-----------------------------------------------------------------------*/
/* ABB_Wait_IBIC_Access()                                                */
/*                                                                       */
/* This function waits for the first IBIC access.                        */
/*                                                                       */
/*-----------------------------------------------------------------------*/
void ABB_Wait_IBIC_Access(void)
{
  #if (ANLG_FAM == 1)
    // Wait 6 OSCAS cycles (100 KHz) for first IBIC access
    // (i.e wait 60us + 10% security marge = 66us)
    wait_ARM_cycles(convert_nanosec_to_cycles(66000));
  #elif ((ANLG_FAM == 2) || (ANLG_FAM == 3))
    // Wait 6 x 32 KHz clock cycles for first IBIC access
    // (i.e wait 187us + 10% security marge = 210us)
    wait_ARM_cycles(convert_nanosec_to_cycles(210000));
  #endif
}


/*-----------------------------------------------------------------------*/
/* ABB_Write_Register_on_page()                                          */
/*                                                                       */
/* This function manages all the spi serial transfer to write to an      */
/* ABB register on a specified page.                                     */
/*                                                                       */
/*-----------------------------------------------------------------------*/
void ABB_Write_Register_on_page(SYS_UWORD16 page, SYS_UWORD16 reg_id, SYS_UWORD16 value)
{
  volatile SYS_UWORD16 status;
  STATUS sem_status;

  #if ((ABB_SEMAPHORE_PROTECTION == 1) || (ABB_SEMAPHORE_PROTECTION == 2) || (ABB_SEMAPHORE_PROTECTION == 3))

  // check if the semaphore has been correctly created and try to obtain it.
  // if the semaphore cannot be obtained, the task is suspended and then resumed
  // as soon as the semaphore is released.

  sem_status = NU_Obtain_Semaphore(&abb_sem, NU_SUSPEND);

  #endif  // ABB_SEMAPHORE_PROTECTION

  // Start spi clock, mask IT for WR and read SPI_REG_STATUS to reset the RE and WE flags.
  SPI_Ready_for_WR
  status = * (volatile SYS_UWORD16 *) SPI_REG_STATUS;

  // set the ABB page for register access
  ABB_SetPage(page);

  // Write value in reg_id
  ABB_WriteRegister(reg_id, value);

  // set the ABB page for register access at page 0
  ABB_SetPage(PAGE0);

  // Stop the SPI clock
  #ifdef SPI_CLK_LOW_POWER
    SPI_CLK_DISABLE
  #endif

  #if ((ABB_SEMAPHORE_PROTECTION == 1) || (ABB_SEMAPHORE_PROTECTION == 2) || (ABB_SEMAPHORE_PROTECTION == 3))
  // release the semaphore only if it has correctly been created.
  if(sem_status == NU_SUCCESS)
  {
    NU_Release_Semaphore(&abb_sem);
  }
  #endif  // ABB_SEMAPHORE_PROTECTION
}


/*-----------------------------------------------------------------------*/
/* ABB_Read_Register_on_page()                                           */
/*                                                                       */
/* This function manages all the spi serial transfer to read one         */
/* ABB register on a specified page.                                     */
/*                                                                       */
/* Returns the real data value of the register.                          */
/*                                                                       */
/*-----------------------------------------------------------------------*/
SYS_UWORD16 ABB_Read_Register_on_page(SYS_UWORD16 page, SYS_UWORD16 reg_id)
{
  volatile SYS_UWORD16 status;
  STATUS sem_status;
  SYS_UWORD16 reg_val;

  #if ((ABB_SEMAPHORE_PROTECTION == 1) || (ABB_SEMAPHORE_PROTECTION == 2) || (ABB_SEMAPHORE_PROTECTION == 3))

  // check if the semaphore has been correctly created and try to obtain it.
  // if the semaphore cannot be obtained, the task is suspended and then resumed
  // as soon as the semaphore is released.

  sem_status = NU_Obtain_Semaphore(&abb_sem, NU_SUSPEND);

  #endif  // ABB_SEMAPHORE_PROTECTION

  // Start spi clock, mask IT for RD and WR and read SPI_REG_STATUS to reset the RE and WE flags.
  SPI_Ready_for_RDWR
  status = * (volatile SYS_UWORD16 *) SPI_REG_STATUS;

  /* set the ABB page for register access */
  ABB_SetPage(page);

  /* Read selected ABB register */
  reg_val = ABB_ReadRegister(reg_id);

  /* set the ABB page for register access at page 0 */
  ABB_SetPage(PAGE0);

  // Stop the SPI clock
  #ifdef SPI_CLK_LOW_POWER
    SPI_CLK_DISABLE
  #endif

  #if ((ABB_SEMAPHORE_PROTECTION == 1) || (ABB_SEMAPHORE_PROTECTION == 2) || (ABB_SEMAPHORE_PROTECTION == 3))
  // release the semaphore only if it has correctly been created.
  if(sem_status == NU_SUCCESS)
  {
    NU_Release_Semaphore(&abb_sem);
  }
  #endif  // ABB_SEMAPHORE_PROTECTION

  return (reg_val);     // Return result
}

/*------------------------------------------------------------------------*/
/* ABB_free_13M()                                                         */
/*                                                                        */
/* This function sets the 13M clock working in ABB. A wait loop           */
/* is required to allow first slow access to ABB clock register.          */
/*                                                                        */
/* WARNING !! : this function must not be protected by semaphore !!       */
/*                                                                        */
/*------------------------------------------------------------------------*/
void ABB_free_13M(void)
{
  volatile SYS_UWORD16 status;

  // Start spi clock, mask IT for WR and read SPI_REG_STATUS to reset the RE and WE flags.
  SPI_Ready_for_WR
  status = * (volatile SYS_UWORD16 *) SPI_REG_STATUS;

  ABB_SetPage(PAGE0);

  // This transmission frees the CLK13 in ABB.
  ABB_WriteRegister(TOGBR2, 0x08);

  // Wait for first IBIC access
  ABB_Wait_IBIC_Access();

  // SW Workaround : This transmission has to be done twice.
  ABB_WriteRegister(TOGBR2, 0x08);

  // Wait for first IBIC access
  ABB_Wait_IBIC_Access();

  // Stop the SPI clock
  #ifdef SPI_CLK_LOW_POWER
    SPI_CLK_DISABLE
  #endif
}


/*------------------------------------------------------------------------*/
/* ABB_stop_13M()                                                         */
/*                                                                        */
/* This function stops the 13M clock in ABB.                              */
/*                                                                        */
/*------------------------------------------------------------------------*/
void ABB_stop_13M(void)
{
  volatile SYS_UWORD16 status;

  // Start spi clock, mask IT for WR and read SPI_REG_STATUS to reset the RE and WE flags.
  SPI_Ready_for_WR
  status = * (volatile SYS_UWORD16 *) SPI_REG_STATUS;

  ABB_SetPage(PAGE0);

  // Set ACTIVMCLK = 0.
  ABB_WriteRegister(TOGBR2, 0x04);

  // Wait for first IBIC access
  ABB_Wait_IBIC_Access();

  // Stop the SPI clock
  #ifdef SPI_CLK_LOW_POWER
    SPI_CLK_DISABLE
  #endif
}


/*------------------------------------------------------------------------*/
/* ABB_Read_Status()                                                      */
/*                                                                        */
/* This function reads and returns the value of VRPCSTS ABB register.     */
/*                                                                        */
/*------------------------------------------------------------------------*/
SYS_UWORD16 ABB_Read_Status(void)
{
  volatile SYS_UWORD16 status;
  STATUS sem_status;
  SYS_UWORD16 reg_val;

  #if ((ABB_SEMAPHORE_PROTECTION == 2) || (ABB_SEMAPHORE_PROTECTION == 3))

  // check if the semaphore has been correctly created and try to obtain it.
  // if the semaphore cannot be obtained, the task is suspended and then resumed
  // as soon as the semaphore is released.

  sem_status = NU_Obtain_Semaphore(&abb_sem, NU_SUSPEND);

  #endif  // ABB_SEMAPHORE_PROTECTION

  // Start spi clock, mask IT for WR and read SPI_REG_STATUS to reset the RE and WE flags.
  SPI_Ready_for_WR
  status = * (volatile SYS_UWORD16 *) SPI_REG_STATUS;

  ABB_SetPage(PAGE0);

  #if (ANLG_FAM == 1) || (ANLG_FAM == 2)
    ABB_SetPage(PAGE0);
    reg_val = ABB_ReadRegister(VRPCSTS);
  #elif (ANLG_FAM == 3)
    ABB_SetPage(PAGE1);
    reg_val = ABB_ReadRegister(VRPCCFG);
  #endif

  // Stop the SPI clock
  #ifdef SPI_CLK_LOW_POWER
    SPI_CLK_DISABLE
  #endif

  #if ((ABB_SEMAPHORE_PROTECTION == 2) || (ABB_SEMAPHORE_PROTECTION == 3))
  // release the semaphore only if it has correctly been created.
  if(sem_status == NU_SUCCESS)
  {
    NU_Release_Semaphore(&abb_sem);
  }
  #endif  // ABB_SEMAPHORE_PROTECTION

  return (reg_val);
}

/*------------------------------------------------------------------------*/
/* ABB_on()                                                               */
/*                                                                        */
/* This function configures ABB registers to work in ON condition         */
/*                                                                        */
/*------------------------------------------------------------------------*/
void ABB_on(SYS_UWORD16 modules, SYS_UWORD8 bRecoveryFlag)
{
  volatile SYS_UWORD16 status;
  STATUS sem_status;
  #if ((ANLG_FAM == 2) || (ANLG_FAM == 3))
    SYS_UWORD32 reg;
  #endif

  #if (ABB_SEMAPHORE_PROTECTION == 3)

  // check if the semaphore has been correctly created and try to obtain it.
  // if the semaphore cannot be obtained, the task is suspended and then resumed
  // as soon as the semaphore is released.

  sem_status = NU_Obtain_Semaphore(&abb_sem, NU_SUSPEND);

  #endif  // ABB_SEMAPHORE_PROTECTION

  // a possible cause of the recovery is that ABB is on Oscas => switch from Oscas to CLK13
  if (bRecoveryFlag)
  {
    // RESTITUTE 13MHZ CLOCK TO ABB
    //---------------------------------------------------
    ABB_free_13M();

    // RESTITUTE 13MHZ CLOCK TO ABB AGAIN (C.F. BUG1719)
    //---------------------------------------------------
    ABB_free_13M();
  }

  // Start spi clock, mask IT for RD and WR and read SPI_REG_STATUS to reset the RE and WE flags.
  SPI_Ready_for_RDWR
  status = * (volatile SYS_UWORD16 *) SPI_REG_STATUS;

  ABB_SetPage(PAGE0);

  // This transmission disables MADC,AFC,VDL,VUL modules.
  ABB_WriteRegister(TOGBR1, 0x0155);

  #if (ANLG_FAM == 1)
    // This transmission disables Band gap fast mode Enable BB charge.
    ABB_WriteRegister(VRPCCTL2, 0x1fc);

    /* *********** DC/DC enabling selection ************************************************************** */
    // This transmission changes the register page in OMEGA for usp to pg1.
    ABB_SetPage(PAGE1);

    /* Insert here accesses to modify DC/DC parameters. Default is a switching frequency of 240 Khz */
    {
      SYS_UWORD8 vrpcctrl3_data;

      #if (CHIPSET == 9) || (CHIPSET == 10) || (CHIPSET == 11)
        vrpcctrl3_data = 0x007d;  // core voltage 1.4V for C035
      #else
        vrpcctrl3_data = 0x00bd;  // core voltage 1.8V for C05
      #endif

    if(modules & DCDC)    // check if the DCDC is enabled
    {
       vrpcctrl3_data |= 0x0002; // set DCDCEN
    }

    // This access disables the DCDC.
    ABB_WriteRegister(VRPCCTRL3, vrpcctrl3_data);
    }

    /* ************************  SELECTION OF TEST MODE FOR ABB **************************************** */
    /* This test configuration allows visibility on BULENA,BULON,BDLON,BDLENA on test pins               */
    /* ***************************************************************************************************/
    #if (BOARD==6)&& (ANLG_FAM==1)  //BUG01967 to remove access to TAPCTRL   (EVA4 board and Nausica)
      // This transmission enables Omega test register.
      ABB_WriteRegister(TAPCTRL, 0x01);

      // This transmission select Omega test instruction.
      ABB_WriteRegister(TAPREG, TSPTEST1);

      // This transmission disables Omega test register.
      ABB_WriteRegister(TAPCTRL, 0x00);
    #endif
    /* *************************************************************************************************** */

    if (!bRecoveryFlag)    // Check recovery status from L1, prevent G23 SIM issue
    {
      // This transmission changes SIM power supply to 3 volts.
      ABB_WriteRegister(VRPCCTRL1, 0x45);
    }

    ABB_SetPage(PAGE0);

    // This transmission enables selected OMEGA modules.
    ABB_WriteRegister(TOGBR1, (modules & ~DCDC) >> 6);

    if(modules & MADC)   // check if the ADC is enabled
    {
      // This transmission connects the resistive divider to MB and BB.
      ABB_WriteRegister(BCICTL1, 0x0005);
    }
  #elif ((ANLG_FAM == 2) || (ANLG_FAM == 3))
    // Restore the ABB checks and debouncing if start on TESTRESETZ

    // This transmission changes the register page in the ABB for usp to pg1.
    ABB_SetPage(PAGE1);

    // This transmission sets the AFCCK to CKIN/2.
    ABB_WriteRegister(AFCCTLADD, 0x01);

    // This transmission enables the tapreg.
    ABB_WriteRegister(TAPCTRL, 0x01);

    // This transmission enables access to page 2.
    ABB_WriteRegister(TAPREG, 0x01b);

    // This transmission changes the register page in the ABB for usp to pg2.
    ABB_SetPage(PAGE2);

    #if (ANLG_FAM == 2)
    // Restore push button environment
    ABB_WriteRegister(0x3C, 0x07);

    #elif (ANLG_FAM == 3)

    // Restore push button environment
    ABB_WriteRegister(0x3C, 0xBF);

      /* ************************  SELECTION OF BBCFG CONFIG FOR ABB 3 PG1_0 *******************************/
        #if (ANLG_PG == S_PG_10)                    // SYREN PG1.0 ON ESAMPLE
          ABB_WriteRegister(BBCFG, C_BBCFG);         // Initialize transmit register
        #endif
    // This transmission enables access to page 0.
    ABB_SetPage(PAGE0);

    // reset bit MSKINT1 , if set by TESTRESET
    reg=ABB_ReadRegister(VRPCSTS) & 0xffe;

    ABB_WriteRegister(VRPCSTS, reg);

    ABB_SetPage(PAGE2);

    // Restore default for BG behavior in sleep mode
    ABB_WriteRegister(VRPCAUX, 0xBF);

    // Restore default for deboucing length
    ABB_WriteRegister(VRPCLDO, 0x00F);

    // Restore default for INT1 generation, wait time in switch on, checks in switch on
    ABB_WriteRegister(VRPCABBTST, 0x0002);

    #endif

    // This transmission changes the register page in the ABB for usp to pg1.
    ABB_SetPage(PAGE1);

    // This transmission sets tapinst to id code.
    ABB_WriteRegister(TAPREG, 0x0001);

    // This transmission disables TAPREG access.
    ABB_WriteRegister(TAPCTRL, 0x00);

    // enable BB battery charge BCICONF register, enable test mode to track BDLEN and BULEN windows
    // This transmission enables BB charge and BB bridge connection for BB measurements.
#if ENABLE_BACKUP_BATTERY
    ABB_WriteRegister(BCICONF, 0x060);
#else
    ABB_WriteRegister(BCICONF, 0x000);
#endif

     /* ************************  SELECTION OF BBCFG CONFIG FOR ABB 3 PG2_0 *******************************/
      #if (ANLG_FAM == 3)
        #if (ANLG_PG == S_PG_20)                     // SYREN PG2.0 ON EVACONSO
           ABB_WriteRegister(BBCFG, C_BBCFG);         // Initialize transmit register
        #endif
      #endif

    /* ************************  SELECTION OF TEST MODE FOR ABB ******************************************/
    /* This test configuration allows visibility on test pins  TAPCTRL has not to be reset                */
    /* ****************************************************************************************************/

    // This transmission enables the tapreg.
    ABB_WriteRegister(TAPCTRL, 0x01);

    // This transmission select ABB test instruction.
    ABB_WriteRegister(TAPREG, TSPEN);

    // This transmission changes the register page in ABB for usp to pg0.
    ABB_SetPage(PAGE0);

    // This transmission enables selected ABB modules.
    ABB_WriteRegister(TOGBR1, modules >> 6);

    // enable MB & BB resistive bridges for measurements
    if(modules & MADC)       // check if the ADC is enabled
    {
      // This transmission connects the resistive divider to MB and BB.
      ABB_WriteRegister(BCICTL1, 0x0001);
    }

    /********* Sleep definition part ******************/
    // This transmission changes the register page in the ABB for usp to pg1.
    #if (ANLG_FAM == 2)
      ABB_SetPage(PAGE1);

      // update the Delay needed by the ABB before going in deep sleep, and clear previous delay value.
      reg = ABB_ReadRegister(VRPCCFG) & 0x1e0;

      ABB_WriteRegister(VRPCCFG, (SLPDLY | reg));

      // update the ABB mask sleep register (regulator disabled in deep sleep), and clear previous mask value.
      reg = ABB_ReadRegister(VRPCMSK) & 0x1e0;
      ABB_WriteRegister(VRPCMSK, (MASK_SLEEP_MODE | reg));
    #elif (ANLG_FAM == 3)
         Syren_Sleep_Config(NORMAL_SLEEP,SLEEP_BG,SLPDLY);
    #endif
    //  This transmission changes the register page in the ABB for usp to pg0.
    ABB_SetPage(PAGE0);
  #endif

  // SW workaround for initialization of the audio parts of the ABB to avoid white noise
  // C.f. BUG1941
  // Set VDLR and VULR bits
  // Write TOGBR1 register
  // This transmission enables selected ABB modules.
  ABB_WriteRegister(TOGBR1, 0x0A);

  // wait for 1 ms
  wait_ARM_cycles(convert_nanosec_to_cycles(1000000));

  // Reset VDLS and VULS bits
  // Write TOGBR1 register
  // This transmission enables selected ABB modules.
  ABB_WriteRegister(TOGBR1, 0x05);

  // Stop the SPI clock
  #ifdef SPI_CLK_LOW_POWER
    SPI_CLK_DISABLE
  #endif

  #if (ABB_SEMAPHORE_PROTECTION == 3)
  // release the semaphore only if it has correctly been created.
  if(sem_status == NU_SUCCESS)
  {
    NU_Release_Semaphore(&abb_sem);
  }
  #endif  // ABB_SEMAPHORE_PROTECTION
}


/*-----------------------------------------------------------------------*/
/* ABB_Read_ADC()                                                        */
/*                                                                       */
/* This function manages all the spi serial transfer to read all the     */
/* ABB ADC conversion channels.                                          */
/* Stores the result in Buff parameter.                                  */
/*                                                                       */
/*-----------------------------------------------------------------------*/
void ABB_Read_ADC(SYS_UWORD16 *Buff)
{
  volatile SYS_UWORD16 status;
  STATUS sem_status;

  #if (ABB_SEMAPHORE_PROTECTION == 3)

  // check if the semaphore has been correctly created and try to obtain it.
  // if the semaphore cannot be obtained, the task is suspended and then resumed
  // as soon as the semaphore is released.

  sem_status = NU_Obtain_Semaphore(&abb_sem, NU_SUSPEND);

  #endif  // ABB_SEMAPHORE_PROTECTION

  // Start spi clock, mask IT for RD and WR and read SPI_REG_STATUS to reset the RE and WE flags.
  SPI_Ready_for_RDWR
  status = * (volatile SYS_UWORD16 *) SPI_REG_STATUS;

  // This transmission changes the register page in the ABB for usp to pg0.
  ABB_SetPage(PAGE0);

  /* Read all ABB ADC registers */
  *Buff++ = ABB_ReadRegister(VBATREG);
  *Buff++ = ABB_ReadRegister(VCHGREG);
  *Buff++ = ABB_ReadRegister(ICHGREG);
  *Buff++ = ABB_ReadRegister(VBKPREG);
  *Buff++ = ABB_ReadRegister(ADIN1REG);
  *Buff++ = ABB_ReadRegister(ADIN2REG);
  *Buff++ = ABB_ReadRegister(ADIN3REG);

  #if (ANLG_FAM == 1)
    *Buff++ = ABB_ReadRegister(ADIN4XREG);
    *Buff++ = ABB_ReadRegister(ADIN5YREG);
  #elif (ANLG_FAM == 2)
    *Buff++ = ABB_ReadRegister(ADIN4REG);
  #elif (ANLG_FAM == 3)
    *Buff++ = ABB_ReadRegister(ADIN4REG);
    *Buff++ = ABB_ReadRegister(ADIN5REG);
  #endif   // ANLG_FAM

  // Stop the SPI clock
  #ifdef SPI_CLK_LOW_POWER
    SPI_CLK_DISABLE
  #endif

  #if (ABB_SEMAPHORE_PROTECTION == 3)
  // release the semaphore only if it has correctly been created.
  if(sem_status == NU_SUCCESS)
  {
    NU_Release_Semaphore(&abb_sem);
  }
  #endif  // ABB_SEMAPHORE_PROTECTION
}


/*-----------------------------------------------------------------------*/
/* ABB_Conf_ADC()                                                        */
/*                                                                       */
/* This function manages all the spi serial transfer to:                 */
/*  - select the ABB ADC channels to be converted                        */
/*  - enable/disable EOC interrupt                                       */
/*                                                                       */
/*-----------------------------------------------------------------------*/
void ABB_Conf_ADC(SYS_UWORD16 Channels, SYS_UWORD16 ItVal)
{
  volatile SYS_UWORD16 status;
  STATUS sem_status;
  SYS_UWORD16 reg_val;

  #if (ABB_SEMAPHORE_PROTECTION == 3)

  // check if the semaphore has been correctly created and try to obtain it.
  // if the semaphore cannot be obtained, the task is suspended and then resumed
  // as soon as the semaphore is released.

  sem_status = NU_Obtain_Semaphore(&abb_sem, NU_SUSPEND);

  #endif  // ABB_SEMAPHORE_PROTECTION

  // Start spi clock, mask IT for RD and WR and read SPI_REG_STATUS to reset the RE and WE flags.
  SPI_Ready_for_RDWR
  status = * (volatile SYS_UWORD16 *) SPI_REG_STATUS;

  // This transmission changes the register page in the ABB for usp to pg0.
  ABB_SetPage(PAGE0);

  /* select ADC channels to be converted */
  #if (ANLG_FAM == 1)
    ABB_WriteRegister(MADCCTRL1, Channels);
  #elif ((ANLG_FAM == 2) || (ANLG_FAM == 3))
    ABB_WriteRegister(MADCCTRL, Channels);
  #endif

  reg_val = ABB_ReadRegister(ITMASK);

  // This transmission configure the End Of Conversion IT without modifying other bits in the same register.
  if(ItVal == EOC_INTENA)
    ABB_WriteRegister(ITMASK, reg_val & EOC_INTENA);
  else if(ItVal == EOC_INTMASK)
    ABB_WriteRegister(ITMASK, reg_val | EOC_INTMASK);

  // Stop the SPI clock
  #ifdef SPI_CLK_LOW_POWER
    SPI_CLK_DISABLE
  #endif

  #if (ABB_SEMAPHORE_PROTECTION == 3)
  // release the semaphore only if it has correctly been created.
  if(sem_status == NU_SUCCESS)
  {
    NU_Release_Semaphore(&abb_sem);
  }
  #endif  // ABB_SEMAPHORE_PROTECTION
}


/*------------------------------------------------------------------------*/
/* ABB_sleep()                                                            */
/*                                                                        */
/* This function disables the DCDC and returns to PAGE 0. It stops then   */
/* the 13MHz clock in ABB. A wait loop s required to allow                */
/* first slow access to ABB clock register.                               */
/*                                                                        */
/* WARNING !! : this function must not be protected by semaphore !!       */
/*                                                                        */
/* Returns AFC value.                                                     */
/*                                                                        */
/*------------------------------------------------------------------------*/
SYS_UWORD32 ABB_sleep(SYS_UWORD8 sleep_performed, SYS_WORD16 afc)
{
  volatile SYS_UWORD16 status;
  SYS_UWORD32 afcout_index;
  volatile SYS_UWORD16 nb_it;
  SYS_UWORD16 reg_val;

  // table for AFC allowed values during Sleep mode. First 5th elements
  // are related to positive AFC values, last 5th to negative ones.
  SYS_UWORD32 Afcout_T[10]= {0x0f,0x1f,0x3f,0x7f,0xff,0x00,0x01,0x03,0x07,0x0f};

  // Start spi clock, mask IT for RD and WR and read SPI_REG_STATUS to reset the RE and WE flags.
  SPI_Ready_for_RDWR
  status = * (volatile SYS_UWORD16 *) SPI_REG_STATUS;

  // COMPUTATION AND PROGRAMMING OF AFC VALUE
  //---------------------------------------------------
  if(afc & 0x1000)
    afcout_index = ((afc + 512)>>10) + 1;
  else
    afcout_index = (afc  + 512)>>10;

  if (sleep_performed == FRAME_STOP)   // Big sleep
  {
    #if ((ANLG_FAM == 2) || (ANLG_FAM == 3))
      //////////// ADD HERE IOTA or SYREN CONFIGURATION FOR BIG SLEEP ////////////////////////////
    #endif

  }
  else                                  // Deep sleep
  {
    #if(ANLG_FAM == 1)
      // SELECTION OF AFC TEST MODE FOR OMEGA
      //---------------------------------------------------
      // This test configuration allows access on the AFCOUT register
      ABB_SetPage(PAGE1);

      // This transmission enables OMEGA test register.
      ABB_WriteRegister(TAPCTRL, 0x01);

      // This transmission selects OMEGA test instruction.
      ABB_WriteRegister(TAPREG, AFCTEST);

      // Set AFCOUT to 0.
      ABB_WriteRegister(AFCOUT, 0x00 >> 6);

      ABB_SetPage(PAGE0);

    #elif (ANLG_FAM == 2)
      // This configuration allows access on the AFCOUT register
      ABB_SetPage(PAGE1);

      // Read AFCCTLADD value and enable USP access to AFCOUT register
      reg_val = (ABB_ReadRegister(AFCCTLADD) | 0x04);

      ABB_WriteRegister(AFCCTLADD, reg_val);

      // Set AFCOUT to 0.
      ABB_WriteRegister(AFCOUT, 0x00);

#if ENABLE_BACKUP_BATTERY
      // Read BCICONF value	and cut the measurement bridge of BB cut the BB charge.
      reg_val = ABB_ReadRegister(BCICONF) & 0x039f;

      ABB_WriteRegister(BCICONF, reg_val);
#endif

      // Disable the ABB test mode
      ABB_WriteRegister(TAPCTRL, 0x00);

      ABB_SetPage(PAGE0);

      // Read BCICTL1 value and cut the measurement bridge of MB.
      reg_val = ABB_ReadRegister(BCICTL1) & 0x03fe;

      ABB_WriteRegister(BCICTL1, reg_val);
    #endif

     #if (ANLG_FAM == 3)              // Nothing to be done as MB and BB measurement bridges are automatically disconnected
                                              // in Syren during sleep mode. BB charge stays enabled
       ABB_SetPage(PAGE1);                    // Initialize transmit reg_num. This transmission
                                              // change the register page in IOTA for usp to pg1

       ABB_WriteRegister(TAPCTRL, 0x00);      // Disable Syren test mode

       ABB_SetPage(PAGE0);
     #endif

    // switch off MADC, AFC, AUXDAC, VOICE.
    ABB_WriteRegister(TOGBR1, 0x155);

    // Switch off Analog supply LDO
    //-----------------------------
    #if (ANLG_FAM == 1)
      ABB_SetPage(PAGE1);

      // Read VRPCCTL3 register value and switch off VR3.
      reg_val = ABB_ReadRegister(VRPCCTRL3) & 0x3df;

      ABB_WriteRegister(VRPCCTRL3, reg_val);

    #elif (ANLG_FAM == 2)
      // Read VRPCSTS register value and extract status of meaningfull inputs.
      reg_val = ABB_ReadRegister(VRPCSTS) & 0x0070;

    #ifdef CONFIG_TARGET_LUNA
      if (reg_val == 0x30 && ABB_sleep_allowed)
    #else
      if (reg_val == 0x30)
    #endif
      {
        // start the SLPDLY counter in order to switch the ABB in sleep mode. This transmission sets IOTA sleep bit.
        ABB_WriteRegister(VRPCDEV, 0x02);
      }

      // Dummy transmission to clean of ABB bus. This transmission accesses IOTA address 0 in "read".
      ABB_WriteRegister(0x0000 | 0x0001, 0x0000);

    #elif (ANLG_FAM == 3)
       // In Syren there is no need to check for VRPCCFG as wake up prioritys are changed
       // start the SLPDLY counter in order to switch the ABB in sleep mode
       ABB_WriteRegister(VRPCDEV,0x02);     // Initialize transmit reg_num. This transmission
                                            // set Syren sleep bit
/*
      // Dummy transmission to clean of ABB bus. This transmission accesses SYREN address 0 in "read".
      ABB_WriteRegister(0x0000 | 0x0001, 0x0000);
*/
    #endif

    // Switch to low frequency clock
    ABB_stop_13M();
  }

  // Stop the SPI clock
  #ifdef SPI_CLK_LOW_POWER
    SPI_CLK_DISABLE
  #endif

#if (OP_L1_STANDALONE == 1)
   #if (CHIPSET == 12)
       // GPIO_InitAllPull(ALL_ONE);  // enable all GPIO internal pull
       // workaround to set APLL_DIV_CLK( internal PU) at high level
       // by default APLL_DIV_CLK is low pulling 80uA on VRIO
//       *(SYS_UWORD16*) (0xFFFFFD90)= 0x01;//CNTL_APLL_DIV_CLK -> APLL_CLK_DIV != 0
//       *(SYS_UWORD16*) (0xFFFEF030)= 0x10;// DPLL mode
   #endif
#endif
  return(Afcout_T[afcout_index]);
}


/*------------------------------------------------------------------------*/
/* ABB_wakeup()                                                           */
/*                                                                        */
/* This function sets the 13MHz clock working in ABB. A wait loop         */
/* is required to allow first slow access to ABB clock register.          */
/* Then it re-enables DCDC and returns to PAGE 0.                         */
/*                                                                        */
/* WARNING !! : this function must not be protected by semaphore !!       */
/*                                                                        */
/*------------------------------------------------------------------------*/
void ABB_wakeup(SYS_UWORD8 sleep_performed, SYS_WORD16 afc)
{
  volatile SYS_UWORD16 status;
  SYS_UWORD16 reg_val;

  // Start spi clock, mask IT for RD and WR and read SPI_REG_STATUS to reset the RE and WE flags.
  SPI_Ready_for_RDWR
  status = * (volatile SYS_UWORD16 *) SPI_REG_STATUS;

  if (sleep_performed == FRAME_STOP)   // Big sleep
  {
    #if ((ANLG_FAM == 2) || (ANLG_FAM == 3))
      //////////// ADD HERE IOTA or SYREN CONFIGURATION FOR BIG SLEEP WAKEUP ////////////////////////////
    #endif
  }
  else                                  // Deep sleep
  {
    #if (OP_L1_STANDALONE == 1)
      #if (CHIPSET == 12)
         // restore context from
         // workaround to set APLL_DIV_CLK( internal PU) at high level
         // by default APLL_DIV_CLK is low pulling 80uA on VRIO
//         *(SYS_UWORD16*) (0xFFFFFD90)= 0x00;//CNTL_APLL_DIV_CLK -> APLL_DIV_CLK != 0
//         *(SYS_UWORD16*) (0xFFFEF030)= 0x00;// DPLL mode
      #endif
    #endif

    // Restitutes 13MHZ Clock to ABB
    ABB_free_13M();

    // Switch ON Analog supply LDO
    #if (ANLG_FAM == 1)
      ABB_SetPage(PAGE1);

      // Read VRPCCTL3 register value and switch on VR3.
      reg_val = ABB_ReadRegister(VRPCCTRL3) | 0x020;

      ABB_WriteRegister(VRPCCTRL3, reg_val);
      ABB_SetPage(PAGE0);
    #endif

    // This transmission switches on MADC, AFC.
    ABB_WriteRegister(TOGBR1, 0x280);

    // This transmission sets the AUXAFC2.
    ABB_WriteRegister(AUXAFC2, ((afc>>10) & 0x7));

    // This transmission sets the AUXAFC1.
    ABB_WriteRegister(AUXAFC1, (afc & 0x3ff));

    #if (ANLG_FAM == 1)
      // Remove AFC test mode
      ABB_SetPage(PAGE1);

      // This transmission select Omega test instruction.
      ABB_WriteRegister(TAPREG, TSPTEST1);

      // Disable test mode selection
      // This transmission disables Omega test register.
      ABB_WriteRegister(TAPCTRL, 0x00 >> 6);

      ABB_SetPage(PAGE0);

    #elif (ANLG_FAM == 2)
      ABB_SetPage(PAGE1);

      // Read AFCCTLADD register value and disable USP access to AFCOUT register.
      reg_val = ABB_ReadRegister(AFCCTLADD) & ~0x04;

      ABB_WriteRegister(AFCCTLADD, reg_val);

#if ENABLE_BACKUP_BATTERY
      // Read BCICONF register value and enable BB measurement bridge enable BB charge.
      reg_val = ABB_ReadRegister(BCICONF) | 0x0060;

      ABB_WriteRegister(BCICONF, reg_val);
#endif


      /* *************************************************************************************************** */
      // update the Delay needed by the ABB before going in deep sleep, and clear previous delay value.
      reg_val = ABB_ReadRegister(VRPCCFG) & 0x1e0;
      ABB_WriteRegister(VRPCCFG, (SLPDLY | reg_val));

      // Enable the ABB test mode
      ABB_WriteRegister(TAPCTRL, 0x01);
      ABB_WriteRegister(TAPREG, TSPEN);
      ABB_SetPage(PAGE0);

      // Read BCICTL1 register value and enable MB measurement bridge and cut the measurement bridge of MB.
      reg_val = ABB_ReadRegister(BCICTL1) | 0x0001;

      ABB_WriteRegister(BCICTL1, reg_val);
    #endif

  #if (ANLG_FAM == 3)

    ABB_SetPage(PAGE1);

    /* *************************************************************************************************** */
    // update the Delay needed by the ABB before going in deep sleep, and clear previous delay value.
    reg_val = ABB_ReadRegister(VRPCCFG) & 0x1e0;
    ABB_WriteRegister(VRPCCFG, (SLPDLY | reg_val));

    /* ************************  SELECTION OF TEST MODE FOR ABB=3 *****************************************/
    /* This test configuration allows visibility on test pins  TAPCTRL has not to be reset                */
    /* ****************************************************************************************************/

    ABB_WriteRegister(TAPCTRL, 0x01);             // Initialize the transmit register
                                                  // This transmission enables IOTA test register

    ABB_WriteRegister(TAPREG, TSPEN);
                                                  // This transmission select IOTA test instruction
                                                  // This transmission select IOTA test instruction
    /**************************************************************************************************** */

    ABB_SetPage(PAGE0);                          // Initialize transmit reg_num. This transmission
  #endif
  }

  // Stop the SPI clock
  #ifdef SPI_CLK_LOW_POWER
    SPI_CLK_DISABLE
  #endif
}

/*------------------------------------------------------------------------*/
/* ABB_wa_VRPC()                                                          */
/*                                                                        */
/* This function initializes the VRPCCTRL1 or VRPCSIM register            */
/* according to the ABB used.                                             */
/*                                                                        */
/*------------------------------------------------------------------------*/
void ABB_wa_VRPC(SYS_UWORD16 value)
{
  volatile SYS_UWORD16 status;
  STATUS sem_status;

  #if ((ABB_SEMAPHORE_PROTECTION == 1) || (ABB_SEMAPHORE_PROTECTION == 2) || (ABB_SEMAPHORE_PROTECTION == 3))

  // check if the semaphore has been correctly created and try to obtain it.
  // if the semaphore cannot be obtained, the task is suspended and then resumed
  // as soon as the semaphore is released.

  sem_status = NU_Obtain_Semaphore(&abb_sem, NU_SUSPEND);

  #endif  // ABB_SEMAPHORE_PROTECTION

  // Start spi clock, mask IT for WR and read SPI_REG_STATUS to reset the RE and WE flags.
  SPI_Ready_for_WR
  status = * (volatile SYS_UWORD16 *) SPI_REG_STATUS;

  ABB_SetPage(PAGE1);

  #if (ANLG_FAM == 1)
    // This transmission initializes the VRPCCTL1 register.
    ABB_WriteRegister(VRPCCTRL1, value);

  #elif (ANLG_FAM == 2)
    // This transmission initializes the VRPCSIM register.
    ABB_WriteRegister(VRPCSIM, value);

  #elif (ANLG_FAM == 3)
    // This transmission initializes the VRPCSIMR register.
    ABB_WriteRegister(VRPCSIMR, value);

  #endif

  ABB_SetPage(PAGE0);

  // Stop the SPI clock
  #ifdef SPI_CLK_LOW_POWER
    SPI_CLK_DISABLE
  #endif

  #if ((ABB_SEMAPHORE_PROTECTION == 1) || (ABB_SEMAPHORE_PROTECTION == 2) || (ABB_SEMAPHORE_PROTECTION == 3))
  // release the semaphore only if it has correctly been created.
  if(sem_status == NU_SUCCESS)
  {
    NU_Release_Semaphore(&abb_sem);
  }
  #endif  // ABB_SEMAPHORE_PROTECTION
}


/*-----------------------------------------------------------------------*/
/* ABB_Write_Uplink_Data()                                               */
/*                                                                       */
/* This function uses the SPI to write to ABB uplink buffer.             */
/*                                                                       */
/*-----------------------------------------------------------------------*/
void ABB_Write_Uplink_Data(SYS_UWORD16 *TM_ul_data)
{
  SYS_UWORD8 i;
  volatile SYS_UWORD16 status;
  STATUS sem_status;

  #if ((ABB_SEMAPHORE_PROTECTION == 2) || (ABB_SEMAPHORE_PROTECTION == 3))

  // check if the semaphore has been correctly created and try to obtain it.
  // if the semaphore cannot be obtained, the task is suspended and then resumed
  // as soon as the semaphore is released.

  sem_status = NU_Obtain_Semaphore(&abb_sem, NU_SUSPEND);

  #endif  // ABB_SEMAPHORE_PROTECTION

  // Start spi clock, mask IT for WR and read SPI_REG_STATUS to reset the RE and WE flags.
  SPI_Ready_for_WR
  status = * (volatile SYS_UWORD16 *) SPI_REG_STATUS;

  // Select Page 0 for TOGBR2
  ABB_SetPage(PAGE0);

  // Initialize pointer of burst buffer 1 : IBUFPTR is bit 10 of TOGBR2
  ABB_WriteRegister(TOGBR2, 0x10);

  // Clear, assuming that it works like IBUFPTR of Vega
  ABB_WriteRegister(TOGBR2, 0x0);

  // Write the ramp data
  for (i=0;i<16;i++)
    ABB_WriteRegister(BULDATA1_2, TM_ul_data[i]>>6);

  // Stop the SPI clock
  #ifdef SPI_CLK_LOW_POWER
    SPI_CLK_DISABLE
  #endif

  #if ((ABB_SEMAPHORE_PROTECTION == 2) || (ABB_SEMAPHORE_PROTECTION == 3))
  // release the semaphore only if it has correctly been created.
  if(sem_status == NU_SUCCESS)
  {
    NU_Release_Semaphore(&abb_sem);
  }
  #endif  // ABB_SEMAPHORE_PROTECTION
}

//////////////////////// IDEV-INLO integration of sleep mode for Syren ///////////////////////////////////////

#if (ANLG_FAM == 3)

  // Syren Sleep configuration function --------------------------
  void Syren_Sleep_Config(SYS_UWORD16 sleep_type,SYS_UWORD16 bg_select, SYS_UWORD16 sleep_delay)
  {
    volatile SYS_UWORD16 status,sl_ldo_stat;

    ABB_SetPage(PAGE1);                            // Initialize transmit register. This transmission
                                                   // change the register page in ABB for usp to pg1

    ABB_WriteRegister(VRPCCFG, sleep_delay);       // write delay value

    sl_ldo_stat = ((sleep_type<<9|bg_select<<8) & 0x0374);

    ABB_WriteRegister(VRPCMSKSLP, sl_ldo_stat);     // write sleep ldo configuration

    ABB_SetPage(PAGE0);                            // Initialize transmit register. This transmission
                                                   // change the register page in ABB for usp to pg0
  }
#endif


#if (OP_L1_STANDALONE == 0)
/*-----------------------------------------------------------------------*/
/* ABB_Power_Off()                                                       */
/*                                                                       */
/* This function uses the SPI to switch off the ABB.                     */
/*                                                                       */
/*-----------------------------------------------------------------------*/
void ABB_Power_Off(void)
{
  // Wait until all necessary actions are performed (write in FFS, etc...) to power-off the board (empirical value - 30 ticks).
  NU_Sleep (30);

  // Wait also until <ON/OFF> key is released.
  // This is needed to avoid, if the power key is pressed for a long time, to switch
  // ON-switch OFF the mobile, until the power key is released.
  #if((ANLG_FAM == 1) || (ANLG_FAM == 2))
    while ((ABB_Read_Status() & ONREFLT) == PWR_OFF_KEY_PRESSED) {
  #elif(ANLG_FAM == 3)
    while ((ABB_Read_Register_on_page(PAGE1, VRPCCFG) & PWOND) == PWR_OFF_KEY_PRESSED) {
  #endif

  NU_Sleep (1); }

  BZ_KeyBeep_OFF();

  #if(ANLG_FAM == 1)
    ABB_Write_Register_on_page(PAGE0, VRPCCTL2, 0x00EE);
  #elif((ANLG_FAM == 2) || (ANLG_FAM == 3))
    ABB_Write_Register_on_page(PAGE0, VRPCDEV, 0x0001);
  #endif
}
#endif