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.

275 lines
6.8 KiB
C

/**
* \file
*
* \brief Chip-specific PLL 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_PLL_H_INCLUDED
#define XMEGA_PLL_H_INCLUDED
#include <compiler.h>
/**
* \weakgroup pll_group
* @{
*/
#define NR_PLLS 1
#define PLL_MIN_HZ 10000000UL
#define PLL_MAX_HZ 200000000UL
#define PLL_NR_OPTIONS 0
enum pll_source {
//! 2 MHz Internal RC Oscillator
PLL_SRC_RC2MHZ = OSC_PLLSRC_RC2M_gc,
//! 32 MHz Internal RC Oscillator
PLL_SRC_RC32MHZ = OSC_PLLSRC_RC32M_gc,
//! External Clock Source
PLL_SRC_XOSC = OSC_PLLSRC_XOSC_gc,
};
#define pll_get_default_rate(pll_id) \
pll_get_default_rate_priv(CONFIG_PLL##pll_id##_SOURCE, \
CONFIG_PLL##pll_id##_MUL, \
CONFIG_PLL##pll_id##_DIV)
/**
* \internal
* \brief Return clock rate for specified PLL settings.
*
* \note Due to the hardware implementation of the PLL, \a div must be 4 if the
* 32 MHz RC oscillator is used as reference and 1 otherwise. The reference must
* be above 440 kHz, and the output between 10 and 200 MHz.
*
* \param src ID of the PLL's reference source oscillator.
* \param mul Multiplier for the PLL.
* \param div Divisor for the PLL.
*
* \retval Output clock rate from PLL.
*/
static inline uint32_t pll_get_default_rate_priv(enum pll_source src,
unsigned int mul, unsigned int div)
{
uint32_t rate;
switch (src) {
case PLL_SRC_RC2MHZ:
rate = 2000000UL;
Assert(div == 1);
break;
case PLL_SRC_RC32MHZ:
#ifdef CONFIG_OSC_RC32_CAL //32MHz oscillator is calibrated to another frequency
rate = CONFIG_OSC_RC32_CAL / 4;
#else
rate = 8000000UL;
#endif
Assert(div == 4);
break;
case PLL_SRC_XOSC:
rate = osc_get_rate(OSC_ID_XOSC);
Assert(div == 1);
break;
default:
break;
}
Assert(rate >= 440000UL);
rate *= mul;
Assert(rate >= PLL_MIN_HZ);
Assert(rate <= PLL_MAX_HZ);
return rate;
}
struct pll_config {
uint8_t ctrl;
};
/**
* \note The XMEGA PLL hardware uses hard-wired input dividers, so the
* user must ensure that \a div is set as follows:
* - If \a src is PLL_SRC_32MHZ, \a div must be set to 4.
* - Otherwise, \a div must be set to 1.
*/
static inline void pll_config_init(struct pll_config *cfg, enum pll_source src,
unsigned int div, unsigned int mul)
{
Assert(mul >= 1 && mul <= 31);
if (src == PLL_SRC_RC32MHZ) {
Assert(div == 4);
} else {
Assert(div == 1);
}
/* Initialize the configuration */
cfg->ctrl = src | (mul << OSC_PLLFAC_gp);
}
#define pll_config_defaults(cfg, pll_id) \
pll_config_init(cfg, \
CONFIG_PLL##pll_id##_SOURCE, \
CONFIG_PLL##pll_id##_DIV, \
CONFIG_PLL##pll_id##_MUL)
static inline void pll_config_read(struct pll_config *cfg, unsigned int pll_id)
{
Assert(pll_id < NR_PLLS);
cfg->ctrl = OSC.PLLCTRL;
}
static inline void pll_config_write(const struct pll_config *cfg,
unsigned int pll_id)
{
Assert(pll_id < NR_PLLS);
OSC.PLLCTRL = cfg->ctrl;
}
/**
* \note If a different PLL reference oscillator than those enabled by
* \ref sysclk_init() is used, the user must ensure that the desired reference
* is enabled prior to calling this function.
*/
static inline void pll_enable(const struct pll_config *cfg,
unsigned int pll_id)
{
irqflags_t flags;
Assert(pll_id < NR_PLLS);
flags = cpu_irq_save();
pll_config_write(cfg, pll_id);
OSC.CTRL |= OSC_PLLEN_bm;
cpu_irq_restore(flags);
}
/*! \note This will not automatically disable the reference oscillator that is
* configured for the PLL.
*/
static inline void pll_disable(unsigned int pll_id)
{
irqflags_t flags;
Assert(pll_id < NR_PLLS);
flags = cpu_irq_save();
OSC.CTRL &= ~OSC_PLLEN_bm;
cpu_irq_restore(flags);
}
static inline bool pll_is_locked(unsigned int pll_id)
{
Assert(pll_id < NR_PLLS);
return OSC.STATUS & OSC_PLLRDY_bm;
}
static inline void pll_enable_source(enum pll_source src)
{
switch (src) {
case PLL_SRC_RC2MHZ:
break;
case PLL_SRC_RC32MHZ:
if (!osc_is_ready(OSC_ID_RC32MHZ)) {
osc_enable(OSC_ID_RC32MHZ);
osc_wait_ready(OSC_ID_RC32MHZ);
#ifdef CONFIG_OSC_AUTOCAL_RC32MHZ_REF_OSC
if (CONFIG_OSC_AUTOCAL_RC32MHZ_REF_OSC
!= OSC_ID_USBSOF) {
osc_enable(CONFIG_OSC_AUTOCAL_RC32MHZ_REF_OSC);
osc_wait_ready(CONFIG_OSC_AUTOCAL_RC32MHZ_REF_OSC);
}
osc_enable_autocalibration(OSC_ID_RC32MHZ,
CONFIG_OSC_AUTOCAL_RC32MHZ_REF_OSC);
#endif
}
break;
case PLL_SRC_XOSC:
if (!osc_is_ready(OSC_ID_XOSC)) {
osc_enable(OSC_ID_XOSC);
osc_wait_ready(OSC_ID_XOSC);
}
break;
default:
Assert(false);
break;
}
}
static inline void pll_enable_config_defaults(unsigned int pll_id)
{
struct pll_config pllcfg;
if (pll_is_locked(pll_id)) {
return; // Pll already running
}
switch (pll_id) {
#ifdef CONFIG_PLL0_SOURCE
case 0:
pll_enable_source(CONFIG_PLL0_SOURCE);
pll_config_init(&pllcfg,
CONFIG_PLL0_SOURCE,
CONFIG_PLL0_DIV,
CONFIG_PLL0_MUL);
break;
#endif
default:
Assert(false);
break;
}
pll_enable(&pllcfg, pll_id);
while (!pll_is_locked(pll_id));
}
//! @}
#endif /* XMEGA_PLL_H_INCLUDED */