/**
 * \file
 *
 * \brief Chip-specific oscillator management functions
 *
 * Copyright (c) 2012 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
 *    AVR 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 CHIP_OSC_H_INCLUDED
#define CHIP_OSC_H_INCLUDED

#include <board.h>

#ifdef __cplusplus
extern "C" {
#endif


/**
 * \weakgroup osc_group
 * @{
 */

//! \name Oscillator identifiers
//@{
#define OSC_ID_OSC0             0       //!< External Oscillator 0
#define OSC_ID_OSC32            1       //!< External 32 kHz oscillator
#define OSC_ID_RC32K            2       //!< Internal 32 kHz RC oscillator
#define OSC_ID_RC80M            3       //!< Internal 80 MHz RC oscillator
#define OSC_ID_RCSYS            4       //!< Internal System RC oscillator
#define OSC_ID_RC1M             5       //!< Internal 1 MHz RC oscillator
#define OSC_ID_RCFAST           6       //!< Internal 4-8-12 MHz RC oscillator
//@}

//! \name OSC0 mode values
//@{
//! External clock connected to XIN
#define OSC_MODE_EXTERNAL       0
//! Crystal connected to XIN/XOUT
#define OSC_MODE_XTAL           1
//@}

//! \name OSC32 mode values
//@{
//! External clock connected to XIN32
#define OSC32_MODE_EXTERNAL     BSCIF_OSCCTRL32_MODE(0)
//! Crystal connected to XIN32/XOUT32
#define OSC32_MODE_XTAL         BSCIF_OSCCTRL32_MODE(1)
//! Crystal connected to XIN32/XOUT32 in high current mode
#define OSC32_MODE_XTAL_HC      BSCIF_OSCCTRL32_MODE(4)
//@}

//! \name OSC0 startup values
//@{
//! 0 cycles
#define OSC_STARTUP_0           SCIF_OSCCTRL0_STARTUP(0)
//! 64 cycles (560 us)
#define OSC_STARTUP_64          SCIF_OSCCTRL0_STARTUP(1)
//! 128 cycles (1.1 ms)
#define OSC_STARTUP_128         SCIF_OSCCTRL0_STARTUP(2)
//! 2048 cycles (18 ms)
#define OSC_STARTUP_2048        SCIF_OSCCTRL0_STARTUP(3)
//! 4096 cycles (36 ms)
#define OSC_STARTUP_4096        SCIF_OSCCTRL0_STARTUP(4)
//! 8192 cycles (71 ms)
#define OSC_STARTUP_8192        SCIF_OSCCTRL0_STARTUP(5)
//! 16384 cycles (142 ms)
#define OSC_STARTUP_16384       SCIF_OSCCTRL0_STARTUP(6)
//@}

//! \name OSC32 startup values
//@{
//! 0 cycles
#define OSC32_STARTUP_0         BSCIF_OSCCTRL32_STARTUP(0)
//! 128 cycles (1.1 ms)
#define OSC32_STARTUP_128       BSCIF_OSCCTRL32_STARTUP(1)
//! 8192 cycles (72.3 ms)
#define OSC32_STARTUP_8192      BSCIF_OSCCTRL32_STARTUP(2)
//! 16384 cycles (143 ms)
#define OSC32_STARTUP_16384     BSCIF_OSCCTRL32_STARTUP(3)
//! 65536 cycles (570 ms)
#define OSC32_STARTUP_65536     BSCIF_OSCCTRL32_STARTUP(4)
//! 131072 cycles (1.1 s)
#define OSC32_STARTUP_131072    BSCIF_OSCCTRL32_STARTUP(5)
//! 262144 cycles (2.3 s)
#define OSC32_STARTUP_262144    BSCIF_OSCCTRL32_STARTUP(6)
//! 524288 cycles (4.6 s)
#define OSC32_STARTUP_524288    BSCIF_OSCCTRL32_STARTUP(7)
//@}

/**
 * \def OSC0_STARTUP_TIMEOUT
 * \brief Number of slow clock cycles to wait for OSC0 to start
 *
 * This is the number of slow clock cycles corresponding to
 * OSC0_STARTUP_VALUE with an additional 25% safety margin. If the
 * oscillator isn't running when this timeout has expired, it is assumed
 * to have failed to start.
 */
/**
 * \def OSC0_MODE_VALUE
 * \brief Board-dependent value written to the MODE bitfield of
 * PM_OSCCTRL(0)
 */
/**
 * \def OSC0_STARTUP_VALUE
 * \brief Board-dependent value written to the STARTUP bitfield of
 * PM_OSCCTRL(0)
 */

#if defined(BOARD_OSC0_STARTUP_US)
# if BOARD_OSC0_STARTUP_US == 0
#  define OSC0_STARTUP_VALUE    OSC_STARTUP_0
#  define OSC0_STARTUP_TIMEOUT  8
# elif BOARD_OSC0_STARTUP_US <= 560
#  define OSC0_STARTUP_VALUE    OSC_STARTUP_64
#  define OSC0_STARTUP_TIMEOUT  80
# elif BOARD_OSC0_STARTUP_US <= 1100
#  define OSC0_STARTUP_VALUE    OSC_STARTUP_128
#  define OSC0_STARTUP_TIMEOUT  160
# elif BOARD_OSC0_STARTUP_US <= 18000
#  define OSC0_STARTUP_VALUE    OSC_STARTUP_2048
#  define OSC0_STARTUP_TIMEOUT  2560
# elif BOARD_OSC0_STARTUP_US <= 36000
#  define OSC0_STARTUP_VALUE    OSC_STARTUP_4096
#  define OSC0_STARTUP_TIMEOUT  5120
# elif BOARD_OSC0_STARTUP_US <= 71000
#  define OSC0_STARTUP_VALUE    OSC_STARTUP_8192
#  define OSC0_STARTUP_TIMEOUT  10240
# elif BOARD_OSC0_STARTUP_US <= 142000
#  define OSC0_STARTUP_VALUE    OSC_STARTUP_16384
#  define OSC0_STARTUP_TIMEOUT  20480
# else
#  error BOARD_OSC0_STARTUP_US is too high
# endif
# if BOARD_OSC0_IS_XTAL == true
#  define OSC0_MODE_VALUE       OSC_MODE_XTAL
#  if BOARD_OSC0_HZ < 900000
#   define OSC0_GAIN_VALUE      SCIF_OSCCTRL0_GAIN(0)
#  elif BOARD_OSC0_HZ < 3000000
#   define OSC0_GAIN_VALUE      SCIF_OSCCTRL0_GAIN(1)
#  elif BOARD_OSC0_HZ < 8000000
#   define OSC0_GAIN_VALUE      SCIF_OSCCTRL0_GAIN(2)
#  else
#   define OSC0_GAIN_VALUE      SCIF_OSCCTRL0_GAIN(3)
#  endif
# else
#  define OSC0_MODE_VALUE       OSC_MODE_EXTERNAL
# endif
#else
# if defined(BOARD_OSC0_HZ)
#  error BOARD_OSC0_STARTUP_US must be defined by the board code
# endif
# ifdef __DOXYGEN__
#  define OSC0_STARTUP_VALUE     UNDEFINED
#  define OSC0_STARTUP_TIMEOUT   UNDEFINED
#  define OSC0_MODE_VALUE        UNDEFINED
# endif
#endif

#if defined(BOARD_OSC32_STARTUP_US)
# if BOARD_OSC32_STARTUP_US == 0
#  define OSC32_STARTUP_VALUE    OSC32_STARTUP_0
# elif BOARD_OSC32_STARTUP_US <= 1100
#  define OSC32_STARTUP_VALUE    OSC32_STARTUP_128
# elif BOARD_OSC32_STARTUP_US <= 72300
#  define OSC32_STARTUP_VALUE    OSC32_STARTUP_8192
# elif BOARD_OSC32_STARTUP_US <= 143000
#  define OSC32_STARTUP_VALUE    OSC32_STARTUP_16384
# elif BOARD_OSC32_STARTUP_US <= 570000
#  define OSC32_STARTUP_VALUE    OSC32_STARTUP_65536
# elif BOARD_OSC32_STARTUP_US <= 1100000
#  define OSC32_STARTUP_VALUE    OSC32_STARTUP_131072
# elif BOARD_OSC32_STARTUP_US <= 2300000
#  define OSC32_STARTUP_VALUE    OSC32_STARTUP_262144
# elif BOARD_OSC32_STARTUP_US <= 4600000
#  define OSC32_STARTUP_VALUE    OSC32_STARTUP_524288
# else
#  error BOARD_OSC32_STARTUP_US is too high
# endif
# if BOARD_OSC32_IS_XTAL == true
#  define OSC32_MODE_VALUE       OSC32_MODE_XTAL
# else
#  define OSC32_MODE_VALUE       OSC32_MODE_EXTERNAL
# endif
#else
# if defined(BOARD_OSC32_HZ)
#  error BOARD_OSC32_STARTUP_US must be defined by the board code
# endif
# ifdef __DOXYGEN__
#  define OSC32_STARTUP_VALUE     UNDEFINED
#  define OSC32_STARTUP_TIMEOUT   UNDEFINED
#  define OSC32_MODE_VALUE        UNDEFINED
# endif
#endif
  
#if (!defined CONFIG_RCFAST_FRANGE)
#   define CONFIG_RCFAST_FRANGE    0   /* default frequency Range is 4 MHz */
#endif

/**
 * \name Board-specific configuration parameters
 * The following definitions must be provided by the board code for all
 * working oscillators on the board.
 */
//@{
/**
 * \def BOARD_OSC0_HZ
 * \brief Clock frequency of OSC0 in Hz
 */
/**
 * \def BOARD_OSC0_STARTUP_US
 * \brief Startup time of OSC0 in microseconds
 */
/**
 * \def BOARD_OSC0_IS_XTAL
 * \brief OSC0 uses a crystal, not an external clock
 */
/**
 * \def BOARD_OSC32_HZ
 * \brief Clock frequency of OSC32 in Hz
 */
/**
 * \def BOARD_OSC32_STARTUP_US
 * \brief Startup time of OSC32 in microseconds
 */
/**
 * \def BOARD_OSC32_IS_XTAL
 * \brief OSC32 uses a crystal, not an external clock
 */
/**
 * \def BOARD_OSC32_PINSEL
 * \brief If set to 1, use XIN32_2/XOUT32_2 pins for OSC32
 *
 * If not defined, the primary XIN32/XOUT32 pins are used.
 */
#ifndef BOARD_OSC32_PINSEL
# define BOARD_OSC32_PINSEL     0
#endif

/**
 * \name RC oscillator frequency limits
 * The frequency of the internal RC oscillators may drift a bit as a
 * result of temperature changes. These definitions provide upper and
 * lower limits which may be used to calculate upper and lower limits of
 * timeouts, derived clock frequencies, etc.
 */
//@{
//! Nominal frequency of RCSYS in Hz
#define OSC_RCSYS_NOMINAL_HZ    115000
//! Minimum frequency of RCSYS in Hz
#define OSC_RCSYS_MIN_HZ        100000
//! Maximum frequency of RCSYS in Hz
#define OSC_RCSYS_MAX_HZ        120000
//! Nominal frequency of RC32K in Hz
#define OSC_RC32K_NOMINAL_HZ    32768
//! Minimum frequency of RC32K in Hz
#define OSC_RC32K_MIN_HZ        20000
//! Maximum frequency of RC32K in Hz
#define OSC_RC32K_MAX_HZ        44000
//! Nominal frequency of RC80M in Hz
#define OSC_RC80M_NOMINAL_HZ   80000000
//! Nominal frequency of RCFAST in Hz when configured at 4 MHz
#define OSC_RCFAST4M_NOMINAL_HZ  4000000
//! Nominal frequency of RCFAST in Hz when configured at 8 MHz
#define OSC_RCFAST8M_NOMINAL_HZ  8000000
//! Nominal frequency of RCFAST in Hz when configured at 12 MHz
#define OSC_RCFAST12M_NOMINAL_HZ  12000000
//! Nominal frequency of RC1M in Hz
#define OSC_RC1M_NOMINAL_HZ    1000000
//@}

#ifndef __ASSEMBLY__

#include <stdbool.h>
#include <stdint.h>
#include "io.h"

extern void osc_priv_enable_osc0(void);
extern void osc_priv_disable_osc0(void);
extern void osc_priv_enable_osc32(void);
extern void osc_priv_disable_osc32(void);
extern void osc_priv_enable_rc32k(void);
extern void osc_priv_disable_rc32k(void);
extern void osc_priv_enable_rc80m(void);
extern void osc_priv_disable_rc80m(void);
extern void osc_priv_enable_rcfast(void);
extern void osc_priv_disable_rcfast(void);
extern void osc_priv_enable_rc1m(void);
extern void osc_priv_disable_rc1m(void);

static inline void osc_enable(uint8_t id)
{
	switch (id) {
#ifdef BOARD_OSC0_HZ
	case OSC_ID_OSC0:
		osc_priv_enable_osc0();
		break;
#endif

#ifdef BOARD_OSC32_HZ
	case OSC_ID_OSC32:
		osc_priv_enable_osc32();
		break;
#endif

	case OSC_ID_RC32K:
		osc_priv_enable_rc32k();
		break;

	case OSC_ID_RC80M:
		osc_priv_enable_rc80m();
		break;

	case OSC_ID_RCSYS:
		/* RCSYS is always running */
		break;

	case OSC_ID_RC1M:
		osc_priv_enable_rc1m();
		break;

	case OSC_ID_RCFAST:
		osc_priv_enable_rcfast();
		break;

	default:
		/* unhandled_case(id); */
		break;
	}
}

static inline void osc_disable(uint8_t id)
{
	switch (id) {
#ifdef BOARD_OSC0_HZ
	case OSC_ID_OSC0:
		osc_priv_disable_osc0();
		break;
#endif

#ifdef BOARD_OSC32_HZ
	case OSC_ID_OSC32:
		osc_priv_disable_osc32();
		break;
#endif

	case OSC_ID_RC32K:
		osc_priv_disable_rc32k();
		break;

	case OSC_ID_RC80M:
		osc_priv_disable_rc80m();
		break;

	case OSC_ID_RCSYS:
		/* RCSYS is always running */
		break;

	case OSC_ID_RC1M:
		osc_priv_disable_rc1m();
		break;

	case OSC_ID_RCFAST:
		osc_priv_disable_rcfast();
		break;

	default:
		/* unhandled_case(id); */
		break;
	}
}

static inline bool osc_is_ready(uint8_t id)
{
	switch (id) {
#ifdef BOARD_OSC0_HZ
	case OSC_ID_OSC0:
		return !!(SCIF->PCLKSR & (SCIF_PCLKSR_OSC0RDY));
#endif

#ifdef BOARD_OSC32_HZ
	case OSC_ID_OSC32:
		return !!(BSCIF->PCLKSR & (BSCIF_PCLKSR_OSC32RDY));
#endif

	case OSC_ID_RC32K:
		return !!(BSCIF->RC32KCR & (BSCIF_RC32KCR_EN));

	case OSC_ID_RC80M:
		return !!(SCIF->RC80MCR & (SCIF_RC80MCR_EN));

	case OSC_ID_RC1M:
		return !!(BSCIF->RC1MCR & (BSCIF_RC1MCR_CLKOE));

	case OSC_ID_RCFAST:
		return !!(SCIF->RCFASTCFG & (SCIF_RCFASTCFG_EN));

	case OSC_ID_RCSYS:
		/* RCSYS is always ready */
		return true;

	default:
		/* unhandled_case(id); */
		return false;
	}
}

static inline uint32_t osc_get_rate(uint8_t id)
{
	switch (id) {
#ifdef BOARD_OSC0_HZ
	case OSC_ID_OSC0:
		return BOARD_OSC0_HZ;
#endif

#ifdef BOARD_OSC32_HZ
	case OSC_ID_OSC32:
		return BOARD_OSC32_HZ;
#endif

	case OSC_ID_RC32K:
		return OSC_RC32K_NOMINAL_HZ;

	case OSC_ID_RC80M:
		return OSC_RC80M_NOMINAL_HZ;

	case OSC_ID_RC1M:
		return OSC_RC1M_NOMINAL_HZ;

	case OSC_ID_RCFAST:
		switch (CONFIG_RCFAST_FRANGE) {
		case 0:
			return OSC_RCFAST4M_NOMINAL_HZ;
		case 1:
			return OSC_RCFAST8M_NOMINAL_HZ;
		case 2:
		default:
			return OSC_RCFAST12M_NOMINAL_HZ;
		}

	case OSC_ID_RCSYS:
		return OSC_RCSYS_NOMINAL_HZ;

	default:
		/* unhandled_case(id); */
		return 0;
	}
}

#endif /* !__ASSEMBLY__ */

//! @}

#ifdef __cplusplus
}
#endif

#endif /* CHIP_OSC_H_INCLUDED */
