/*******************************************************************************
 * (c) Copyright 2009 Actel Corporation.  All rights reserved.
 * 
 * SmartFusion Microcontroller Subsystem (MSS) Watchdog bare metal software
 * driver.
 *
 * SVN $Revision: 1888 $
 * SVN $Date: 2009-12-18 10:58:42 +0000 (Fri, 18 Dec 2009) $
 */
/*=========================================================================*//**
  @section intro_sec Introduction
  The SmartFusion microcontroller subsystem (MSS) includes a watchdog timer used
  to detect system lockups.
  This driver provides a set of functions for controlling the MSS watchdog as
  part of a bare metal system where no operating system is available. These
  drivers can be adapted for use as part of an operating system but the
  implementation of the adaptation layer between this driver and the operating
  system's driver model is outside the scope of this driver.
    
  @section hw_dependencies Hardware Flow Dependencies
  The configuration of all features of the MSS watchdog is covered by this
  driver. There are no dependencies on the hardware flow for configuring the
  SmartFusion MSS watchdog timer.
    
  @section theory_op Theory of Operation
  The watchdog driver uses the SmartFusion "Cortex Microcontroler Software
  Interface Standard - Peripheral Access Layer" (CMSIS-PAL) to access hadware
  registers. You must ensure that the SmartFusion CMSIS-PAL is either included
  in the software toolchain used to build your project or is included in your
  project. The most up-to-date SmartFusion CMSIS-PAL files can be obtained using
  the Actel Firmware Catalog.
  
  The watchdog driver functions are grouped into the following categories:
    - Initialization and cnfiguration
    - Reading the watchdog timer current value and status
    - Refreshing the watchdog timer value
    - Time-out and wake-up interrupts control
  
  The watchdog driver is initialized and configured through a call to the
  MSS_WD_init() function. The parameters passed to MSS_WD_init() function
  specify the watchdog timer configuration. The configuration parameters include
  the value that will be reloaded into the watchdog timer down counter every
  time the watchdog is refreshed. Also included as part of the configuration
  parameters is the optional allowed refresh window. The allowed refresh window
  specifies the maximum allowed current value of the watchdog timer at the time
  of the watchdog is relaoded. Attempting to reload the watchdog timer when its
  value is larger than the allowed refresh window will cause a reset or
  interrupt depending on the watchdog configuration. The allowed refresh window
  can be disabled by specifying an allowed refesh window equal or higher than
  the watchdog reload value.
  The MSS_WD_init() function must be called before any other watchdog driver
  functions can be called with the exception of the MSS_WD_disable() function.
  
  The watchdog timer can be disabled using the MSS_WD_disable() function. Once
  disabled, the watchdog timer can only be reenabled by a power-on reset.
  
  The watchdog timer current value can be read using the MSS_WD_current_value()
  function. The watchdog status can be read using the MSS_WD_status() function.
  These functions are typically required when using the watchdog configured with
  an allowed refresh window to check if a watchdog reload is currently allowed.
  
  The watchdog timer value is reloaded using the MSS_WD_reload() function. The
  value reloaded into the watchdog timer down counter is the value specified as
  parameter to the MSS_WD_init() function.
  
  The watchdog timer can generate interrupts instead of resetting the system
  when its down-counter timer expires. These time-out interrupts are controlled
  using the following functions:
    - MSS_WD_enable_timeout_irq
    - MSS_WD_disable_timeout_irq
    - MSS_WD_clear_timeout_irq
  
  The watchdog timer is external to the Cortex-M3 processor core and operates
  even when the Cortex-M3 is in sleep mode. A wakeup interrupt can be generated
  by the watchdog timer to wakeup the Cortext-M3 when the watchdog timer value
  reaches the allowed refresh window while the Cortex-M3 is in sleep mode. The
  watchdog driver provides the following functions to control wakeup interrupts:
    - MSS_WD_enable_wakeup_irq
    - MSS_WD_disable_wakeup_irq
    - MSS_WD_clear_wakeup_irq
    
 *//*=========================================================================*/

#ifndef MSS_WATCHDOG_H_
#define MSS_WATCHDOG_H_

#ifdef __cplusplus
extern "C" {
#endif 

#include "../../CMSIS/a2fxxxm3.h"

/***************************************************************************//**
 * The MSS_WDOG_RESET_ON_TIMEOUT_MODE macro is one of the possible values for the
 * mode parameter of the WD_init() function. It is used to specify that a reset
 * should occur when the watchdog down counter times out.
 */
#define MSS_WDOG_RESET_ON_TIMEOUT_MODE       (uint32_t)0x00000000U

/***************************************************************************//**
 * The MSS_WDOG_INTERRUPT_ON_TIMEOUT_MODE macro is one of the possible values for
 * the  mode parameter of function the WD_init() function. It is used to specify
 * that a time out interrupt should occur when the watchdog down counter expires.
 */
#define MSS_WDOG_INTERRUPT_ON_TIMEOUT_MODE   (uint32_t)0x00000004U

/***************************************************************************//**
 * The MSS_WDOG_NO_WINDOW macro can be used as the value for the reload_window
 * parameter of the WD_init() function. It is used to specify that no forbidden
 * window will exist for the reload of the watchdog down counter.
 */
#define MSS_WDOG_NO_WINDOW    (uint32_t)0xFFFFFFFFU

/***************************************************************************//**
 * The MSS_WDOG_CTRL_MODE_BIT_MASK macro is a bit mask specifying the bit used to
 * set the watchdog's operating mode within the wathcdog's WDOGCONTROL register.
 */
#define MSS_WDOG_CTRL_MODE_BIT_MASK           (uint32_t)0x00000004U

/***************************************************************************//**
 * The MSS_WDOG_TIMEOUT_IRQ_ENABLE_BIT_MASK macro is a bit mask specifying the bit
 * used to enable the time out interrupt within the watchdog's WDOGCONTROL
 * register.
 */
#define MSS_WDOG_TIMEOUT_IRQ_ENABLE_BIT_MASK  (uint32_t)0x00000001U

/***************************************************************************//**
  The MSS_WDOG_WAKEUP_IRQ_ENABLE_BIT_MASK macro is a bit mask specifying the bit
  used to enable the wake up interrupt within the watchdog's WDOGCONTROL
  register.
 */
#define MSS_WDOG_WAKEUP_IRQ_ENABLE_BIT_MASK   (uint32_t)0x00000002U

/***************************************************************************//**
  The MSS_WDOG_TIMEOUT_IRQ_CLEAR_BIT_MASK macro is a bit mask specifying the bit
  used to clear the time out interrupt within the watchdog's WDOGRIS register.
 */
#define MSS_WDOG_TIMEOUT_IRQ_CLEAR_BIT_MASK   (uint32_t)0x00000001U

/***************************************************************************//**
  The MSS_WDOG_WAKEUP_IRQ_CLEAR_BIT_MASK macro is a bit mask specifying the bit
  used to clear the wake up interrupt within the watchdog's WDOGRIS register.
 */
#define MSS_WDOG_WAKEUP_IRQ_CLEAR_BIT_MASK    (uint32_t)0x00000002U

/***************************************************************************//**
  The MSS_WDOG_REFRESH_KEY macro holds the magic value which will cause a reload
  of the watchdog's down counter when written to the watchdog's WDOGREFRESH
  register.
 */
#define MSS_WDOG_REFRESH_KEY    (uint32_t)0xAC15DE42U

/***************************************************************************//**
  The MSS_WDOG_DISABLE_KEY macro holds the magic value which will disable the
  watchdog if written to the watchdog's WDOGENABLE register.
 */
#define MSS_WDOG_DISABLE_KEY    (uint32_t)0x4C6E55FAU

/***************************************************************************//**
  The MSS_WD_init() function initializes and configures the watchdog timer.
 
  @param load_value
    The load_value parameter specifies the value that will be loaded into the
    watchdog's down counter when the reload command is issued through a call to
    MSS_WD_reload().
                   
  @param reload_window
    The reload_window parameter specifies the time window during which a reload
    of the watchdog counter is allowed. A reload of the watchdog counter should
    only be performed when the watchdog counter value is below the value of the
    reload_window. Reloading the watchdog down counter value before it has
    reached the reload_window will result in an interrupt or reset depending on
    the watchdog's mode.
    The reload window can be disabled by using WDOG_NO_WINDOW for this parameter.
 
  @param mode
    The mode parameter specifies the watchdog's operating mode. It can be either
    MSS_WDOG_RESET_ON_TIMEOUT_MODE or MSS_WDOG_INTERRUPT_ON_TIMEOUT_MODE.
    MSS_WDOG_RESET_ON_TIMEOUT_MODE: a reset will occur if the watchdog timer
    expires.
    MSS_WDOG_INTERRUPT_ON_TIMEOUT_MODE: an NMI interrupt will occur if the
    watchdog timer expires.
 
  @return
    This function does not return a value.
 */
static __INLINE void MSS_WD_init
(
    uint32_t load_value,
    uint32_t reload_window,
    uint32_t mode
)
{
    /* Disable interrupts. */
    WATCHDOG->WDOGCONTROL &= ~(MSS_WDOG_TIMEOUT_IRQ_ENABLE_BIT_MASK | MSS_WDOG_WAKEUP_IRQ_CLEAR_BIT_MASK);
    
    /* Clear any existing interrupts. */
    WATCHDOG->WDOGRIS = MSS_WDOG_TIMEOUT_IRQ_CLEAR_BIT_MASK | MSS_WDOG_WAKEUP_IRQ_CLEAR_BIT_MASK;
    
    /* Configure watchdog with new configuration passed as parameter. */
    WATCHDOG->WDOGMVRP = MSS_WDOG_NO_WINDOW;
    WATCHDOG->WDOGLOAD = load_value;
    WATCHDOG->WDOGCONTROL = (WATCHDOG->WDOGCONTROL & ~MSS_WDOG_CTRL_MODE_BIT_MASK) | (mode & MSS_WDOG_CTRL_MODE_BIT_MASK);
    
    /* Reload watchdog with new load value. */
    WATCHDOG->WDOGREFRESH = MSS_WDOG_REFRESH_KEY;
    
    /* Set allowed window. */
    WATCHDOG->WDOGMVRP = reload_window;
}

/***************************************************************************//**
  The MSS_WD_reload() function causes the watchdog to reload its down counter timer
  with the load value configured through the call to WD_init(). This function 
  must be called regularly to avoid a system reset.
 
  @return
    This function does not return a value.
 */
static __INLINE void MSS_WD_reload( void )
{
    WATCHDOG->WDOGREFRESH = MSS_WDOG_REFRESH_KEY;
}

/***************************************************************************//**
  The MSS_WD_disable() function disables the watchdog.
  Please note that the watchdog can only be reenabled as a result of a power-on
  reset.
 
  @return
    This function does not return a value.
 */
static __INLINE void MSS_WD_disable( void )
{
    WATCHDOG->WDOGENABLE = MSS_WDOG_DISABLE_KEY;
}

/***************************************************************************//**
  The MSS_WD_current_value() function returns the current value of the watchdog's
  down counter.
 
  @return
    This function returns the current value of the watchdog down counter.
 */
static __INLINE uint32_t MSS_WD_current_value( void )
{
    return WATCHDOG->WDOGVALUE;
}

/***************************************************************************//**
  The MSS_WD_status() function returns the status of the watchdog.
 
  @return 
    The MSS_WD_status() function returns the status of the watchdog. A value of
    0 indicates that watchdog counter has reached the forbidden window and that
    a reload should not be done. A value of 1 indicates that the watchdog counter
    is within the permitted window and that a reload is allowed.
 */
static __INLINE uint32_t MSS_WD_status( void )
{
    return WATCHDOG->WDOGSTATUS;
}

/***************************************************************************//**
  The MSS_WD_enable_timeout_irq() function enables the watchdog’s time out
  interrupt which is connected to the Cortex-M3 NMI interrupt.
  The NMI_Handler() function will be called when a watchdog time out occurs. You
  must provide the implementation of the NMI_Handler() function to suit your
  application.
 
  @return
    This function does not return a value.
 
  Example:
  @code
  #include "mss_watchdog.h"
  int main( void )
  {
      MSS_WD_init( 0x10000000, MSS_WDOG_NO_WINDOW, MSS_WDOG_INTERRUPT_ON_TIMEOUT_MODE );
      MSS_WD_enable_timeout_irq();
      for (;;)
      {
          main_task();
      }
  }
 
  void NMI_Handler( void )
  {
      process_timeout();
      MSS_WD_clear_timeout_irq();
  }
  @endcode
 */
static __INLINE void MSS_WD_enable_timeout_irq( void )
{
    WATCHDOG->WDOGCONTROL |= MSS_WDOG_TIMEOUT_IRQ_ENABLE_BIT_MASK;
}

/***************************************************************************//**
  The WD_disable_timeout_irq() function disables the generation of the NMI
  interrupt when the watchdog times out.
 
  @return
    This function does not return a value.
 */
static __INLINE void MSS_WD_disable_timeout_irq( void )
{
    WATCHDOG->WDOGCONTROL &= ~MSS_WDOG_TIMEOUT_IRQ_ENABLE_BIT_MASK;
}

/***************************************************************************//**
  The MSS_WD_enable_wakeup_irq() function enables the SmartFusion wakeup
  interrupt. The WdogWakeup_IRQHandler() function will be called when a wake up
  interrupt occurs. You must provide the implementation of the WdogWakeup_IRQHandler()
  function to suit your application.
 
  @return
    This function does not return a value.
 
  Example:
  @code
  #include "mss_watchdog.h"
  int main( void )
  {
      MSS_WD_init( 0x10000000, MSS_WDOG_NO_WINDOW, MSS_WDOG_INTERRUPT_ON_TIMEOUT_MODE );
      MSS_WD_enable_wakeup_irq();
      for (;;)
      {
          main_task();
          cortex_sleep();
      }
  }
 
  void WdogWakeup_IRQHandler( void )
  {
      process_wakeup();
      MSS_WD_clear_wakeup_irq();
  }
  @endcode
 */
static __INLINE void MSS_WD_enable_wakeup_irq( void )
{
    WATCHDOG->WDOGCONTROL |= MSS_WDOG_WAKEUP_IRQ_ENABLE_BIT_MASK;
    NVIC_EnableIRQ( WdogWakeup_IRQn );
}

/***************************************************************************//**
  The MSS_WD_disable_wakeup_irq() function disables the SmartFusion wakeup
  interrupt. 
 
  @return
    This function does not return a value.
 */
static __INLINE void MSS_WD_disable_wakeup_irq( void )
{
    WATCHDOG->WDOGCONTROL &= ~MSS_WDOG_WAKEUP_IRQ_ENABLE_BIT_MASK;
}

/***************************************************************************//**
  The MSS_WD_clear_timeout_irq() function clears the watchdog’s time out
  interrupt which is connected to the Cortex-M3 NMI interrupt.
  Calling MSS_WD_clear_timeout_irq() results in clearing the Cortex-M3 NMI interrupt.
  Note: The MSS_WD_clear_timeout_irq() function must be called as part of the
  timeout interrupt service routine (ISR) in order to prevent the same interrupt
  event retriggering a call to the wakeup ISR.
 
  @return
    The example below demonstrates the use of the MSS_WD_clear_timeout_irq()
    function as part of the NMI interrupt service routine.

    Example:
  @code
  void NMI_Handler( void )
  {
      process_timeout();
      MSS_WD_clear_timeout_irq();
  }
  @endcode
 */
static __INLINE void MSS_WD_clear_timeout_irq( void )
{
    WATCHDOG->WDOGRIS = MSS_WDOG_TIMEOUT_IRQ_CLEAR_BIT_MASK;
    /*
     * Perform a second write to ensure that the first write completed before 
     * returning from this function. This is to account for posted writes across
     * the AHB matrix. The second write ensures that the first write has
     * completed and that the interrupt line has been de-asserted by the time
     * the function returns. Omitting the second write may result in a delay
     * in the de-assertion of the interrupt line going to the Cortex-M3 and a
     * retriggering of the interrupt.
     */
    WATCHDOG->WDOGRIS = MSS_WDOG_TIMEOUT_IRQ_CLEAR_BIT_MASK;
}

/***************************************************************************//**
  The MSS_WD_clear_wakeup_irq() function clears the wakeup interrupt.
  Note: The MSS_WD_clear_wakeup_irq() function must be called as part of the
  wakeup interrupt service routine (ISR) in order to prevent the same interrupt
  event retriggering a call to the wakeup ISR. This function also clears the
  interrupt in the Cortex-M3 interrupt controller through a call to
  NVIC_ClearPendingIRQ().
 
  @return
    This function does not return a value.

    Example:
    The example below demonstrates the use of the MSS_WD_clear_wakeup_irq() function
    as part of the wakeup interrupt service routine.  
    @code
    void WdogWakeup_IRQHandler( void )
    {
        do_interrupt_processing();
        
        MSS_WD_clear_wakeup_irq();
    }
    @endcode
*/
static __INLINE void MSS_WD_clear_wakeup_irq( void )
{
    WATCHDOG->WDOGRIS = MSS_WDOG_WAKEUP_IRQ_CLEAR_BIT_MASK;
    NVIC_ClearPendingIRQ( WdogWakeup_IRQn );
}
#ifdef __cplusplus
}
#endif

#endif /* MSS_WATCHDOG_H_ */
