/**
 * \file
 *
 * \brief Chip-specific oscillator management functions
 *
 * Copyright (c) 2012 Atmel Corporation. All rights reserved.
 *
 * \asf_license_start
 *
 * \page License
 *
 * 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 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_RCFAST           4       //!< Internal 4-8-12 MHz RCFAST oscillator
#define OSC_ID_RC1M             5       //!< Internal 1 MHz RC oscillator
#define OSC_ID_RCSYS            6       //!< Internal System 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           SCIF_OSCCTRL0_MODE
//@}

//! \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 (143 ms)
#define OSC_STARTUP_16384       SCIF_OSCCTRL0_STARTUP(6)
//! 32768 cycles (285 ms)
#define OSC_STARTUP_32768       SCIF_OSCCTRL0_STARTUP(7)
//@}

//! \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 <= 557
#      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 <= 143000
#      define OSC0_STARTUP_VALUE    OSC_STARTUP_16384
#      define OSC0_STARTUP_TIMEOUT  20480
#   elif BOARD_OSC0_STARTUP_US <= 285000
#      define OSC0_STARTUP_VALUE    OSC_STARTUP_32768
#      define OSC0_STARTUP_TIMEOUT  40960
#   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 < 2000000
#         define OSC0_GAIN_VALUE      SCIF_OSCCTRL0_GAIN(0)
#      elif BOARD_OSC0_HZ < 4000000
#         define OSC0_GAIN_VALUE      SCIF_OSCCTRL0_GAIN(1)
#      elif BOARD_OSC0_HZ < 8000000
#         define OSC0_GAIN_VALUE      SCIF_OSCCTRL0_GAIN(2)
#      elif BOARD_OSC0_HZ < 16000000
#         define OSC0_GAIN_VALUE      SCIF_OSCCTRL0_GAIN(3)
#      else
#         define OSC0_GAIN_VALUE      ((0x1u << 4) | SCIF_OSCCTRL0_GAIN(0))
#      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

// Use 4 MHz frequency range for RCFAST oscillator if config was empty.
#ifndef CONFIG_RCFAST_FRANGE
#define CONFIG_RCFAST_FRANGE    0
#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_SELCURR
 * \brief Crystal current selection for OSC32
 *
 * If not defined, the recommended value (300nA) are used.
 */
#ifndef BOARD_OSC32_SELCURR
#   define BOARD_OSC32_SELCURR     BSCIF_OSCCTRL32_SELCURR(10)
#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 RCFAST4M in Hz
#define OSC_RCFAST4M_NOMINAL_HZ 4000000

//! Nominal frequency of RCFAST8M in Hz
#define OSC_RCFAST8M_NOMINAL_HZ 8000000

//! Nominal frequency of RCFAST12M in Hz
#define OSC_RCFAST12M_NOMINAL_HZ 12000000

//! Nominal frequency of RC1M in Hz
#define OSC_RC1M_NOMINAL_HZ     1000000
//@}

#ifndef __ASSEMBLY__

#include <compiler.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_RCFAST:
		osc_priv_enable_rcfast();
		break;

	case OSC_ID_RC1M:
		osc_priv_enable_rc1m();
		break;

	case OSC_ID_RCSYS:
		/* RCSYS is always running */
		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_RCFAST:
		osc_priv_disable_rcfast();
		break;

	case OSC_ID_RC1M:
		osc_priv_disable_rc1m();
		break;

	case OSC_ID_RCSYS:
		/* RCSYS is always running */
		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->SCIF_PCLKSR & SCIF_PCLKSR_OSC0RDY);
#endif

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

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

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

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

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

	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_RCFAST:
		if (CONFIG_RCFAST_FRANGE == 2) {
			return OSC_RCFAST12M_NOMINAL_HZ;

		} else if (CONFIG_RCFAST_FRANGE == 1) {
			return OSC_RCFAST8M_NOMINAL_HZ;

		} else {
			return OSC_RCFAST4M_NOMINAL_HZ;
		}

	case OSC_ID_RC1M:
		return OSC_RC1M_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 */
