You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
This repo is archived. You can view files and clone it, but cannot push or open issues/pull-requests.

508 lines
13 KiB
C

/**
* \file
*
* \brief Chip-specific oscillator management functions
*
* Copyright (c) 2010-2015 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
*
*/
/*
* Support and FAQ: visit <a href="http://www.atmel.com/design-support/">Atmel Support</a>
*/
#ifndef XMEGA_OSC_H_INCLUDED
#define XMEGA_OSC_H_INCLUDED
#include <compiler.h>
#include <board.h>
/**
* \weakgroup osc_group
*
* \section osc_group_errata Errata
* - Auto-calibration does not work on XMEGA A1 revision H and
* earlier.
* @{
*/
//! \name Oscillator identifiers
//@{
//! 2 MHz Internal RC Oscillator
#define OSC_ID_RC2MHZ OSC_RC2MEN_bm
//! 32 MHz Internal RC Oscillator
#define OSC_ID_RC32MHZ OSC_RC32MEN_bm
//! 32 KHz Internal RC Oscillator
#define OSC_ID_RC32KHZ OSC_RC32KEN_bm
//! External Oscillator
#define OSC_ID_XOSC OSC_XOSCEN_bm
#if XMEGA_E
//! 8 MHz Internal RC Oscillator
# define OSC_ID_RC8MHZ OSC_RC8MEN_bm
#endif
/**
* \brief Reference from USB Start Of Frame
* \note This cannot be enabled or disabled, but can be used as a reference for
* the autocalibration (DFLL).
*/
#define OSC_ID_USBSOF 0xff
//@}
//! \name External oscillator types
//@{
#define XOSC_TYPE_EXTERNAL 0 //!< External clock signal
#define XOSC_TYPE_32KHZ 2 //!< 32.768 kHz resonator on TOSC
#define XOSC_TYPE_XTAL 3 //!< 0.4 to 16 MHz resonator on XTAL
//@}
/**
* \def CONFIG_XOSC_32KHZ_LPM
* \brief Define for enabling Low Power Mode for 32 kHz external oscillator.
*/
#ifdef __DOXYGEN__
# define CONFIG_XOSC_32KHZ_LPM
#endif /* __DOXYGEN__ */
/**
* \def CONFIG_XOSC_STARTUP
* \brief Board-dependent value that determines the number of start-up cycles
* for external resonators, based on BOARD_XOSC_STARTUP_US. This is written to
* the two MSB of the XOSCSEL field of OSC.XOSCCTRL.
*
* \note This is automatically computed from BOARD_XOSC_HZ and
* BOARD_XOSC_STARTUP_US if it is not manually set.
*/
//! \name XTAL resonator start-up cycles
//@{
#define XOSC_STARTUP_256 0 //!< 256 cycle start-up time
#define XOSC_STARTUP_1024 1 //!< 1 k cycle start-up time
#define XOSC_STARTUP_16384 2 //!< 16 k cycle start-up time
//@}
/**
* \def CONFIG_XOSC_RANGE
* \brief Board-dependent value that sets the frequency range of the external
* oscillator. This is written to the FRQRANGE field of OSC.XOSCCTRL.
*
* \note This is automatically computed from BOARD_XOSC_HZ if it is not manually
* set.
*/
//! \name XTAL resonator frequency range
//@{
//! 0.4 to 2 MHz frequency range
#define XOSC_RANGE_04TO2 OSC_FRQRANGE_04TO2_gc
//! 2 to 9 MHz frequency range
#define XOSC_RANGE_2TO9 OSC_FRQRANGE_2TO9_gc
//! 9 to 12 MHz frequency range
#define XOSC_RANGE_9TO12 OSC_FRQRANGE_9TO12_gc
//! 12 to 16 MHz frequency range
#define XOSC_RANGE_12TO16 OSC_FRQRANGE_12TO16_gc
//@}
/**
* \def XOSC_STARTUP_TIMEOUT
* \brief Number of us to wait for XOSC 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.
*/
// If application intends to use XOSC.
#ifdef BOARD_XOSC_HZ
// Get start-up config for XOSC, if not manually set.
# ifndef CONFIG_XOSC_STARTUP
# ifndef BOARD_XOSC_STARTUP_US
# error BOARD_XOSC_STARTUP_US must be configured.
# else
//! \internal Number of start-up cycles for the board's XOSC.
# define BOARD_XOSC_STARTUP_CYCLES \
(BOARD_XOSC_HZ / 1000000 * BOARD_XOSC_STARTUP_US)
# if (BOARD_XOSC_TYPE == XOSC_TYPE_XTAL)
# if (BOARD_XOSC_STARTUP_CYCLES > 16384)
# error BOARD_XOSC_STARTUP_US is too high for current BOARD_XOSC_HZ.
# elif (BOARD_XOSC_STARTUP_CYCLES > 1024)
# define CONFIG_XOSC_STARTUP XOSC_STARTUP_16384
# define XOSC_STARTUP_TIMEOUT (16384*(1000000/BOARD_XOSC_HZ))
# elif (BOARD_XOSC_STARTUP_CYCLES > 256)
# define CONFIG_XOSC_STARTUP XOSC_STARTUP_1024
# define XOSC_STARTUP_TIMEOUT (1024*(1000000/BOARD_XOSC_HZ))
# else
# define CONFIG_XOSC_STARTUP XOSC_STARTUP_256
# define XOSC_STARTUP_TIMEOUT (256*(1000000/BOARD_XOSC_HZ))
# endif
# else /* BOARD_XOSC_TYPE == XOSC_TYPE_XTAL */
# define CONFIG_XOSC_STARTUP 0
# endif
# endif /* BOARD_XOSC_STARTUP_US */
# endif /* CONFIG_XOSC_STARTUP */
// Get frequency range setting for XOSC, if not manually set.
# ifndef CONFIG_XOSC_RANGE
# if (BOARD_XOSC_TYPE == XOSC_TYPE_XTAL)
# if (BOARD_XOSC_HZ < 400000)
# error BOARD_XOSC_HZ is below minimum frequency of 400 kHz.
# elif (BOARD_XOSC_HZ < 2000000)
# define CONFIG_XOSC_RANGE XOSC_RANGE_04TO2
# elif (BOARD_XOSC_HZ < 9000000)
# define CONFIG_XOSC_RANGE XOSC_RANGE_2TO9
# elif (BOARD_XOSC_HZ < 12000000)
# define CONFIG_XOSC_RANGE XOSC_RANGE_9TO12
# elif (BOARD_XOSC_HZ <= 16000000)
# define CONFIG_XOSC_RANGE XOSC_RANGE_12TO16
# else
# error BOARD_XOSC_HZ is above maximum frequency of 16 MHz.
# endif
# else /* BOARD_XOSC_TYPE == XOSC_TYPE_XTAL */
# define CONFIG_XOSC_RANGE 0
# endif
# endif /* CONFIG_XOSC_RANGE */
#endif /* BOARD_XOSC_HZ */
#ifndef __ASSEMBLY__
/**
* \internal
* \brief Enable internal oscillator \a id
*
* Do not call this function directly. Use osc_enable() instead.
*/
static inline void osc_enable_internal(uint8_t id)
{
irqflags_t flags;
Assert(id != OSC_ID_USBSOF);
flags = cpu_irq_save();
OSC.CTRL |= id;
#if (XMEGA_E && CONFIG_SYSCLK_RC8MHZ_LPM)
if(id == OSC_ID_RC8MHZ) {
OSC.CTRL |= OSC_RC8MLPM_bm;
}
#endif
cpu_irq_restore(flags);
}
#if defined(BOARD_XOSC_HZ) || defined(__DOXYGEN__)
/**
* \internal
* \brief Enable external oscillator \a id
*
* Do not call this function directly. Use osc_enable() instead. Also
* note that this function is only available if the board actually has
* an external oscillator crystal.
*/
static inline void osc_enable_external(uint8_t id)
{
irqflags_t flags;
Assert(id == OSC_ID_XOSC);
#ifndef CONFIG_XOSC_32KHZ_LPM
# if (XMEGA_E && (BOARD_XOSC_TYPE == XOSC_TYPE_EXTERNAL) && defined(CONFIG_XOSC_EXTERNAL_PC4))
OSC.XOSCCTRL = OSC_XOSCSEL4_bm;
# else
OSC.XOSCCTRL = BOARD_XOSC_TYPE | (CONFIG_XOSC_STARTUP << 2) |
CONFIG_XOSC_RANGE;
# endif
#else
OSC.XOSCCTRL = BOARD_XOSC_TYPE | (CONFIG_XOSC_STARTUP << 2) |
CONFIG_XOSC_RANGE | OSC_X32KLPM_bm;
#endif /* CONFIG_XOSC_32KHZ_LPM */
flags = cpu_irq_save();
OSC.CTRL |= id;
cpu_irq_restore(flags);
}
#else
static inline void osc_enable_external(uint8_t id)
{
Assert(false); // No external oscillator on the selected board
}
#endif
static inline void osc_disable(uint8_t id)
{
irqflags_t flags;
Assert(id != OSC_ID_USBSOF);
flags = cpu_irq_save();
OSC.CTRL &= ~id;
cpu_irq_restore(flags);
}
static inline void osc_enable(uint8_t id)
{
if (id != OSC_ID_XOSC) {
osc_enable_internal(id);
} else {
osc_enable_external(id);
}
}
static inline bool osc_is_ready(uint8_t id)
{
Assert(id != OSC_ID_USBSOF);
return OSC.STATUS & id;
}
//! \name XMEGA-Specific Oscillator Features
//@{
/**
* \brief Enable DFLL-based automatic calibration of an internal
* oscillator.
*
* The XMEGA features two Digital Frequency Locked Loops (DFLLs) which
* can be used to improve the accuracy of the 2 MHz and 32 MHz internal
* RC oscillators. The DFLL compares the oscillator frequency with a
* more accurate reference clock to do automatic run-time calibration of
* the oscillator.
*
* This function enables auto-calibration for either the 2 MHz or 32 MHz
* internal oscillator using either the 32.768 kHz calibrated internal
* oscillator or an external crystal oscillator as a reference. If the
* latter option is used, the crystal must be connected to the TOSC pins
* and run at 32.768 kHz.
*
* \param id The ID of the oscillator for which to enable
* auto-calibration:
* \arg \c OSC_ID_RC2MHZ or \c OSC_ID_RC32MHZ.
* \param ref_id The ID of the oscillator to use as a reference:
* \arg \c OSC_ID_RC32KHZ or \c OSC_ID_XOSC for internal or external 32 kHz
* reference, respectively.
* \arg \c OSC_ID_USBSOF for 32 MHz only when USB is available and running.
*/
static inline void osc_enable_autocalibration(uint8_t id, uint8_t ref_id)
{
irqflags_t flags;
flags = cpu_irq_save();
switch (id) {
case OSC_ID_RC2MHZ:
#if !XMEGA_E
Assert((ref_id == OSC_ID_RC32KHZ) || (ref_id == OSC_ID_XOSC));
if (ref_id == OSC_ID_XOSC) {
osc_enable(OSC_ID_RC32KHZ);
OSC.DFLLCTRL |= OSC_RC2MCREF_bm;
} else {
OSC.DFLLCTRL &= ~(OSC_RC2MCREF_bm);
}
DFLLRC2M.CTRL |= DFLL_ENABLE_bm;
#endif
break;
case OSC_ID_RC32MHZ:
#if XMEGA_AU || XMEGA_B || XMEGA_C || XMEGA_E
Assert((ref_id == OSC_ID_RC32KHZ)
|| (ref_id == OSC_ID_XOSC)
# if !XMEGA_E
|| (ref_id == OSC_ID_USBSOF)
#endif
);
OSC.DFLLCTRL &= ~(OSC_RC32MCREF_gm);
if (ref_id == OSC_ID_XOSC) {
osc_enable(OSC_ID_RC32KHZ);
OSC.DFLLCTRL |= OSC_RC32MCREF_XOSC32K_gc;
}
else if (ref_id == OSC_ID_RC32KHZ) {
OSC.DFLLCTRL |= OSC_RC32MCREF_RC32K_gc;
}
# if !XMEGA_E
else if (ref_id == OSC_ID_USBSOF) {
/*
* Calibrate 32MRC at 48MHz using USB SOF
* 48MHz / 1kHz = 0xBB80
*/
DFLLRC32M.COMP1 = 0x80;
DFLLRC32M.COMP2 = 0xBB;
OSC.DFLLCTRL |= OSC_RC32MCREF_USBSOF_gc;
}
# endif
#else
Assert((ref_id == OSC_ID_RC32KHZ) ||
(ref_id == OSC_ID_XOSC));
# if defined(OSC_RC32MCREF_gm)
OSC.DFLLCTRL &= ~(OSC_RC32MCREF_gm);
# endif
if (ref_id == OSC_ID_XOSC) {
osc_enable(OSC_ID_RC32KHZ);
# if defined(OSC_RC32MCREF_gm)
OSC.DFLLCTRL |= OSC_RC32MCREF_XOSC32K_gc;
# else
OSC.DFLLCTRL |= OSC_RC32MCREF_bm;
# endif
}
else if (ref_id == OSC_ID_RC32KHZ) {
# if defined(OSC_RC32MCREF_gm)
OSC.DFLLCTRL |= OSC_RC32MCREF_RC32K_gc;
# else
OSC.DFLLCTRL &= ~(OSC_RC32MCREF_bm);
# endif
}
#endif
DFLLRC32M.CTRL |= DFLL_ENABLE_bm;
break;
default:
Assert(false);
break;
}
cpu_irq_restore(flags);
}
/**
* \brief Disable DFLL-based automatic calibration of an internal
* oscillator.
*
* \see osc_enable_autocalibration
*
* \param id The ID of the oscillator for which to disable
* auto-calibration:
* \arg \c OSC_ID_RC2MHZ or \c OSC_ID_RC32MHZ.
*/
static inline void osc_disable_autocalibration(uint8_t id)
{
switch (id) {
case OSC_ID_RC2MHZ:
#if !XMEGA_E
DFLLRC2M.CTRL = 0;
#endif
break;
case OSC_ID_RC32MHZ:
DFLLRC32M.CTRL = 0;
break;
default:
Assert(false);
break;
}
}
/**
* \brief Load a specific calibration value for the specified oscillator.
*
* \param id The ID of the oscillator for which to disable
* auto-calibration:
* \arg \c OSC_ID_RC2MHZ or \c OSC_ID_RC32MHZ.
* \param calib The specific calibration value required:
*
*/
static inline void osc_user_calibration(uint8_t id, uint16_t calib)
{
switch (id) {
case OSC_ID_RC2MHZ:
#if !XMEGA_E
DFLLRC2M.CALA=LSB(calib);
DFLLRC2M.CALB=MSB(calib);
#endif
break;
case OSC_ID_RC32MHZ:
DFLLRC32M.CALA=LSB(calib);
DFLLRC32M.CALB=MSB(calib);
break;
#if XMEGA_E
case OSC_ID_RC8MHZ:
OSC.RC8MCAL=LSB(calib);
break;
#endif
default:
Assert(false);
break;
}
}
//@}
static inline uint32_t osc_get_rate(uint8_t id)
{
Assert(id != OSC_ID_USBSOF);
switch (id) {
case OSC_ID_RC2MHZ:
return 2000000UL;
case OSC_ID_RC32MHZ:
#ifdef CONFIG_OSC_RC32_CAL
return CONFIG_OSC_RC32_CAL;
#else
return 32000000UL;
#endif
case OSC_ID_RC32KHZ:
return 32768UL;
#ifdef BOARD_XOSC_HZ
case OSC_ID_XOSC:
return BOARD_XOSC_HZ;
#endif
default:
Assert(false);
return 0;
}
}
#endif /* __ASSEMBLY__ */
//! @}
#endif /* XMEGA_OSC_H_INCLUDED */