/*****************************************************************************
 *
 * \file
 *
 * \brief Power Manager(PM) driver interface.
 *
 * Copyright (c) 2009 Atmel Corporation. All rights reserved.
 *
 * \asf_license_start
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * 1. Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 * 2. Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * 3. The name of Atmel may not be used to endorse or promote products derived
 *    from this software without specific prior written permission.
 *
 * 4. This software may only be redistributed and used in connection with an
 *    Atmel microcontroller product.
 *
 * THIS SOFTWARE IS PROVIDED BY ATMEL "AS IS" AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT ARE
 * EXPRESSLY AND SPECIFICALLY DISCLAIMED. IN NO EVENT SHALL ATMEL 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.
 *
 * \asf_license_stop
 *
 *****************************************************************************/

#ifndef _PM_UC3C_H_
#define _PM_UC3C_H_

/**
 * \defgroup group_avr32_drivers_pm CPU - PM - Power Manager
 *
 * The Power Manager (PM) provides synchronous clocks to the CPU and the modules and peripherals connected
 * to the HSB and PBx buses. The PM also contains advanced power-saving features and a Reset Controller.
 *
 * \{
 */

#ifdef __cplusplus
extern "C" {
#endif

#include <avr32/io.h>
#include "compiler.h"

//  These defines are missing from or wrong in the toolchain header file ip_xxx.h or part.h
#ifdef AVR32_PM_410_H_INCLUDED
// Optionnal #undef AVR32_PM_UNLOCK_KEY_VALUE if the define values is wrong.
#define AVR32_PM_UNLOCK_KEY_VALUE       0x000000AA
#endif

//! Device-specific data

//! The clock sources of the power manager.
typedef enum
{
  PM_CLK_SRC_SLOW = AVR32_PM_MCSEL_SLOW,
  PM_CLK_SRC_OSC0 = AVR32_PM_MCSEL_OSC0,
  PM_CLK_SRC_OSC1 = AVR32_PM_MCSEL_OSC1,
  PM_CLK_SRC_PLL0 = AVR32_PM_MCSEL_PLL0,
  PM_CLK_SRC_PLL1 = AVR32_PM_MCSEL_PLL1,
  PM_CLK_SRC_RC8M = AVR32_PM_MCSEL_RCOSC8,
  PM_CLK_SRC_RCRIPOSC = AVR32_PM_MCSEL_CRIPOSC,
  PM_CLK_SRC_RC120M = AVR32_PM_MCSEL_RC120M,
  PM_CLK_SRC_INVALID
} pm_clk_src_t;

//! The clock domains of the power manager.
typedef enum
{
  PM_CLK_DOMAIN_0 = AVR32_PM_CLK_GRP_CPU,
  PM_CLK_DOMAIN_1 = AVR32_PM_CLK_GRP_HSB,
  PM_CLK_DOMAIN_2 = AVR32_PM_CLK_GRP_PBA,
  PM_CLK_DOMAIN_3 = AVR32_PM_CLK_GRP_PBB,
  PM_CLK_DOMAIN_4 = AVR32_PM_CLK_GRP_PBC,
  PM_CLK_DOMAIN_INVALID
} pm_clk_domain_t;


//! The possible synchronous clock division ratio.
typedef enum
{
  PM_CKSEL_DIVRATIO_2 = 0,  // Divide the main clock by 2^1
  PM_CKSEL_DIVRATIO_4,      // Divide the main clock by 2^2
  PM_CKSEL_DIVRATIO_8,      // Divide the main clock by 2^3
  PM_CKSEL_DIVRATIO_16,     // Divide the main clock by 2^4
  PM_CKSEL_DIVRATIO_32,     // Divide the main clock by 2^5
  PM_CKSEL_DIVRATIO_64,     // Divide the main clock by 2^6
  PM_CKSEL_DIVRATIO_128,    // Divide the main clock by 2^7
  PM_CKSEL_DIVRATIO_256     // Divide the main clock by 2^8
} pm_divratio_t;

//! The timeguard used for polling (expressed in ticks).
#define PM_POLL_TIMEOUT 100000

//! Define "not supported" for the power manager features.
#define PM_NOT_SUPPORTED (-10000)


//! Unlock PM register macro
#define PM_UNLOCK(reg)  (AVR32_PM.unlock = (unsigned long)(AVR32_PM_UNLOCK_KEY_VALUE << AVR32_PM_UNLOCK_KEY_OFFSET)|(reg))


/*! \name Clock Functions
 */
//! @{

/*! \brief Set the main clock.
 *
 * \param src The clock to use as the main clock.
 *
 * \warning The input clock to use as the main clock must be enabled before calling
 *          this function, otherwise a deadlock will occur.
 *
 * \return Status.
 *   \retval 0 Success.
 *   \retval <0 An error occured when trying to set the main clock.
 */
extern long pm_set_mclk_source(pm_clk_src_t src);

/*! \brief Configure the main clock safety mechanisms.
 *
 * \note Refer to the section Clock Failure Detector of the PM chapter in the datasheet
 *
 * \warning The Critical Path Oscillator (CRIPOSC) must be enabled before
 *          enabling the Over Clock Protection mechanism.
 *
 * \param cfd Enable/disable the Clock Failure Detection mechanism
 * \param final If true, make this configuration definitive
 *
 * \return Status.
 *   \retval =0 Success.
 *   \retval <0 An error occured.
 */
extern long pm_config_mainclk_safety(bool cfd,  bool final);

/*! \brief Set the division ratio for a clock domain.
 *
 * \param clock_domain The clock domain to alter.
 * \param divratio The division ratio to set.
 *
 * \warning Care should be taken that each new frequency of the synchronous clocks
 *          does not exceed the maximum frequency for each clock domain.
 *
 * \return Status.
 *   \retval =0 Success.
 *   \retval <0 An error occured.
 */
extern long pm_set_clk_domain_div(pm_clk_domain_t clock_domain, pm_divratio_t divratio);

/*! \brief Disable the division ratio for a clock domain.
 *
 * \param clock_domain The clock domain to alter.
 *
 * \warning Care should be taken that each new frequency of the synchronous clocks
 *          does not exceed the maximum frequency for each clock domain.
 *
 * \return Status.
 *   \retval =0 Success.
 *   \retval <0 An error occured.
 */
extern long pm_disable_clk_domain_div(pm_clk_domain_t clock_domain);

/*! \brief Wait actively for the clock settings to be effective.
 *
 * \note To avoid an infinite loop, this function checks the clock ready flag
 *  PM_POLL_TIMEOUT times.
 *
 * \return Status.
 *   \retval 0 Success.
 *   \retval <0 Unable to reach a clock ready status within the polling limit.
 */
extern long pm_wait_for_clk_ready(void);

//! @}


/*! \name Module Functions
 */
//! @{

/*! \brief Enable the clock of a module.
 *
 * \param module The module to clock (use one of the defines in the part-specific
 * header file under "toolchain folder"/avr32/inc(lude)/avr32/; depending on the
 * clock domain, look for the sections "CPU clocks", "HSB clocks", "PBx clocks")
 *
 * \return Status.
 *   \retval 0  Success.
 *   \retval <0 An error occured.
 */
extern long pm_enable_module(unsigned long module);

/*! \brief Disable the clock of a module.
 *
 * \param module The module to shut down (use one of the defines in the part-specific
 * header file under "toolchain folder"/avr32/inc(lude)/avr32/; depending on the
 * clock domain, look for the sections "CPU clocks", "HSB clocks", "PBx clocks")
 *
 * \return Status.
 *   \retval 0  Success.
 *   \retval <0 An error occured.
 */
extern long pm_disable_module(unsigned long module);


//! @}

/*! \name Sleep Functions
 */
//! @{

/*! \brief Sets the MCU in the specified sleep mode
 *
 * \note Precautions should be taken before entering sleep mode. Refer to the
 *  datasheet chapter 'Power Manager'.
 *
 * \param sleep_mode The sleep mode index
 */
#define SLEEP(sleep_mode)  {__asm__ __volatile__ ("sleep "STRINGZ(sleep_mode));}

/*! \brief Returns MCU wake cause
 *
 * \return The MCU wake cause which can be masked with the
 *         \c AVR32_PM_WCAUSE_x_MASK bit-masks to isolate specific causes.
 */
#if defined (__GNUC__)
__attribute__((__always_inline__))
#endif
static inline unsigned long pm_get_wake_cause(void)
{
  return AVR32_PM.wcause;
}

/*! \brief Enable one or several asynchronous wake-up source.
 *
 * \param awen_mask Mask of asynchronous wake-up sources (use one of the defines
 *  AVR32_PM_AWEN_xxxxWEN_MASK in the part-specific header file under
 *  "toolchain folder"/avr32/inc(lude)/avr32/)
 */
#if defined (__GNUC__)
__attribute__((__always_inline__))
#endif
static inline void pm_asyn_wake_up_enable(unsigned long awen_mask)
{
  AVR32_PM.awen |= awen_mask;
}

/*! \brief Disable one or several asynchronous wake-up sources
 *
 * \param awen_mask Mask of asynchronous wake-up sources (use one of the defines
 *  AVR32_PM_AWEN_xxxxWEN_MASK in the part-specific header file under
 *  "toolchain folder"/avr32/inc(lude)/avr32/)
 */
#if defined (__GNUC__)
__attribute__((__always_inline__))
#endif
static inline void pm_asyn_wake_up_disable(unsigned long awen_mask)
{
  AVR32_PM.awen &= ~awen_mask;
}

//! @}



/*! \name Reset Functions
 */
//! @{

/*! \brief Returns MCU last reset cause
 *
 * \return The MCU last reset cause which can be masked with the
 *         \c AVR32_PM_RCAUSE_x_MASK bit-masks to isolate specific causes.
 */
#if defined (__GNUC__)
__attribute__((__always_inline__))
#endif
static inline unsigned long pm_get_reset_cause(void)
{
  return AVR32_PM.rcause;
}

//! @}



/*! \name Interrupt Functions
 */
//! @{

/*! \brief Enable power manager interrupts.
 *
 * \param mask the interrupts to enable.
 *
 */
#if defined (__GNUC__)
__attribute__((__always_inline__))
#endif
static inline void pm_enable_interrupts(unsigned long mask)
{
  AVR32_PM.ier |= mask;
}

/*! \brief Disable power manager interrupts.
 *
 * \param mask the interrupts to disable.
 *
 */
#if defined (__GNUC__)
__attribute__((__always_inline__))
#endif
static inline void pm_disable_interrupts(unsigned long mask)
{
  AVR32_PM.idr |= mask;
}

/*! \brief Read the enabled power manager interrupts.
 *
 * \return mask of the enabled interrupts.
 */
#if defined (__GNUC__)
__attribute__((__always_inline__))
#endif
static inline unsigned long pm_get_enabled_interrupts(void)
{
  return(AVR32_PM.imr);
}

/*! \brief Read the interrupts status of the power manager.
 *
 * \return mask of the interrupts that have been triggered.
 */
#if defined (__GNUC__)
__attribute__((__always_inline__))
#endif
static inline unsigned long pm_get_interrupts_status(void)
{
  return(AVR32_PM.isr);
}

/*! \brief Clear raised interrupts from the power manager.
 *
 * \param mask The interrupts to clear.
 */
#if defined (__GNUC__)
__attribute__((__always_inline__))
#endif
static inline void pm_clear_interrupt_status(unsigned long mask)
{
  AVR32_PM.icr |= mask;
}

//! @}



/*! \name Misc Functions
 */
//! @{

/*! \brief Get the PM status
 *
 * \return The content of the PM Status register.
 */
#if defined (__GNUC__)
__attribute__((__always_inline__))
#endif
static inline unsigned long pm_get_status(void)
{
  return AVR32_PM.sr;
}

//! @}

#ifdef __cplusplus
}
#endif

/**
 * \}
 */

#endif  // _PM_UC3C_H_
