blob: 731d487b854ca3a6785c4010d13605e2933eaa17 [file] [log] [blame]
/**
* \file
*
* \brief Chip-specific DFLL definitions
*
* 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_DFLL_H_INCLUDED
#define CHIP_DFLL_H_INCLUDED
#ifdef __cplusplus
extern "C" {
#endif
/**
* \weakgroup dfll_group
* @{
*/
#define NR_DFLLS 1
#define DFLL_MIN_HZ 20000000UL
#define DFLL_MAX_HZ 150000000UL
#define DFLL_MIN_KHZ (DFLL_MIN_HZ / 1000)
#define DFLL_MAX_KHZ (DFLL_MAX_HZ / 1000)
#define DFLL_COARSE_MAX (255)
#define DFLL_FINE_MAX (511)
#define DFLL_FINE_HALF (255)
#define DFLL_CALIB_VALUE (0x0B)
#define DFLL_RANGE0 (0)
#define DFLL_RANGE1 (1)
#define DFLL_RANGE2 (2)
#define DFLL_RANGE3 (3)
#define DFLL_MAX_RANGE1 (110000000)
#define DFLL_MAX_RANGE2 (55000000)
#define DFLL_MAX_RANGE3 (30000000)
#ifndef __ASSEMBLY__
#include "compiler.h"
#include <genclk.h>
#include <osc.h>
typedef enum genclk_source dfll_refclk_t;
struct dfll_config {
struct genclk_config ref_cfg; //!< Reference clock
uint32_t conf; //!< DFLLnCONF
uint32_t mul; //!< DFLLnMUL
uint32_t step; //!< DFLLnSTEP
uint32_t ssg; //!< DFLLnSSG
uint32_t val; //!< DFLLnVAL
uint8_t freq_range; //!< Frequency Range
};
static inline void dfll_config_set_max_step(struct dfll_config *cfg,
uint16_t coarse, uint16_t fine)
{
cfg->step = (SCIF_DFLL0STEP_CSTEP(coarse)
| SCIF_DFLL0STEP_FSTEP(fine));
}
static inline void dfll_priv_set_frequency_range(struct dfll_config *cfg,
uint32_t freq)
{
if (freq < DFLL_MAX_RANGE3){
cfg->freq_range = DFLL_RANGE3;
}
else if (freq < DFLL_MAX_RANGE2){
cfg->freq_range = DFLL_RANGE2;
}
else if (freq < DFLL_MAX_RANGE1){
cfg->freq_range = DFLL_RANGE1;
}
else {
cfg->freq_range = DFLL_RANGE0;
}
cfg->conf &= ~SCIF_DFLL0CONF_RANGE_Msk;
cfg->conf |=SCIF_DFLL0CONF_RANGE(cfg->freq_range);
}
static inline void dfll_config_init_open_loop_mode(struct dfll_config *cfg)
{
genclk_config_defaults(&cfg->ref_cfg, 0);
// Do a sync before reading a dfll conf register
SCIF->SCIF_DFLL0SYNC = SCIF_DFLL0SYNC_SYNC;
while (!(SCIF->SCIF_PCLKSR & SCIF_PCLKSR_DFLL0RDY));
cfg->conf = SCIF->SCIF_DFLL0CONF;
// Select Open Loop Mode
cfg->conf &= ~SCIF_DFLL0CONF_MODE;
// Clear DFLL Frequency Range
cfg->freq_range = 0;
cfg->conf &= ~SCIF_DFLL0CONF_RANGE_Msk;
cfg->conf |= SCIF_DFLL0CONF_RANGE(cfg->freq_range);
cfg->mul = 0;
cfg->step = 0;
cfg->ssg = 0;
cfg->val = 0;
}
#ifdef CONFIG_DFLL0_FREQ
static inline void dfll_config_init_closed_loop_mode(struct dfll_config *cfg,
dfll_refclk_t refclk, uint16_t divide, uint16_t mul)
{
/*
* Set up generic clock source with specified reference clock
* and divider.
*/
genclk_config_defaults(&cfg->ref_cfg, 0);
genclk_config_set_source(&cfg->ref_cfg, refclk);
genclk_config_set_divider(&cfg->ref_cfg, divide);
// Do a sync before reading a dfll conf register
SCIF->SCIF_DFLL0SYNC = SCIF_DFLL0SYNC_SYNC;
while (!(SCIF->SCIF_PCLKSR & SCIF_PCLKSR_DFLL0RDY));
cfg->conf = SCIF->SCIF_DFLL0CONF;
// Select Closed Loop Mode
cfg->conf |= SCIF_DFLL0CONF_MODE;
// Write DFLL Frequency Range
dfll_priv_set_frequency_range(cfg, CONFIG_DFLL0_FREQ);
cfg->mul = mul;
cfg->val = 0;
/*
* Initial step length of 4. If this is set too high, the DFLL
* may fail to lock.
*/
dfll_config_set_max_step(cfg, 4, 4);
cfg->ssg = 0;
}
#endif
static inline uint32_t dfll_priv_get_source_hz(dfll_refclk_t src)
{
/*
* Only handle the cases that actually make sense as a DFLL
* source. The DFLL itself is obviously not one of those cases.
*/
switch (src) {
case GENCLK_SRC_RCSYS:
return OSC_RCSYS_NOMINAL_HZ;
#ifdef BOARD_OSC32_HZ
case GENCLK_SRC_OSC32K:
return BOARD_OSC32_HZ;
#endif
#ifdef BOARD_OSC0_HZ
case GENCLK_SRC_OSC0:
return BOARD_OSC0_HZ;
#endif
case GENCLK_SRC_RC80M:
return OSC_RC80M_NOMINAL_HZ;
case GENCLK_SRC_RC32K:
return OSC_RC32K_NOMINAL_HZ;
default:
/* unhandled_case(src) */
return 0;
}
}
#define dfll_config_defaults(cfg, dfll_id) \
dfll_config_init_closed_loop_mode(cfg, \
CONFIG_DFLL##dfll_id##_SOURCE, \
CONFIG_DFLL##dfll_id##_DIV, \
CONFIG_DFLL##dfll_id##_MUL)
#define dfll_get_default_rate(dfll_id) \
((dfll_priv_get_source_hz(CONFIG_DFLL##dfll_id##_SOURCE) \
* CONFIG_DFLL##dfll_id##_MUL) \
/ CONFIG_DFLL##dfll_id##_DIV)
static inline void dfll_config_set_initial_tuning(struct dfll_config *cfg,
uint16_t coarse, uint16_t fine)
{
cfg->val = (SCIF_DFLL0VAL_COARSE(coarse)
| SCIF_DFLL0VAL_FINE(fine));
}
/**
* \brief Tune the DFLL configuration for a specific target frequency
*
* This will set the initial coarse and fine DFLL tuning to match the
* given target frequency. In open loop mode, this will cause the DFLL
* to run close to the specified frequency, though it may not match
* exactly. In closed loop mode, the DFLL will automatically tune itself
* to the target frequency regardless of the initial tuning, but this
* function may be used to set a starting point close to the desired
* frequency in order to reduce the startup time.
*
* \par Calculating the DFLL frequency
*
* \f{eqnarray*}{
f_{DFLL} &=& \left[f_{min} + \left(f_{max} - f_{min}\right)
\frac{\mathrm{COARSE}}{\mathrm{COARSE}_{max}}\right]
\left(1 + x \frac{\mathrm{FINE}
- \mathrm{FINE}_{half}}{\mathrm{FINE}_{max}}\right)
= f_{coarse} \left(1 + x
\frac{\mathrm{FINE}
- \mathrm{FINE}_{half}}{\mathrm{FINE}_{max}}\right) \\
\mathrm{COARSE} &=& \frac{\left(f_{DFLL} - f_{min}\right)}
{f_{max} - f_{min}} \mathrm{COARSE}_{max} \\
f_{coarse} &=& f_{min} + \left(f_{max} - f_{min}\right)
\frac{\mathrm{COARSE}}{\mathrm{COARSE}_{max}} \\
\mathrm{FINE} &=& \left(10 \frac{f_{DFLL} - f_{coarse}}
{f_{coarse}} + \mathrm{FINE}_{half}\right) / 4
\f}
*
* \param cfg The DFLL configuration to be tuned.
* \param target_hz Target frequency in Hz.
*/
static inline void dfll_config_tune_for_target_hz(struct dfll_config *cfg,
uint32_t target_hz)
{
uint32_t target_khz;
uint32_t coarse_khz;
uint32_t delta_khz;
uint32_t coarse;
uint32_t fine;
target_khz = target_hz / 1000;
coarse = ((target_khz - DFLL_MIN_KHZ) * DFLL_COARSE_MAX)
/ (DFLL_MAX_KHZ - DFLL_MIN_KHZ);
coarse_khz = DFLL_MIN_KHZ + (((DFLL_MAX_KHZ - DFLL_MIN_KHZ)
/ DFLL_COARSE_MAX) * coarse);
delta_khz = target_khz - coarse_khz;
fine = (((delta_khz * DFLL_FINE_MAX) * 2) / coarse_khz) * 5;
fine += DFLL_FINE_HALF;
fine /= 4;
dfll_config_set_initial_tuning(cfg, coarse, fine);
dfll_priv_set_frequency_range(cfg, target_hz);
}
static inline void dfll_config_enable_ssg(struct dfll_config *cfg,
uint16_t amplitude, uint16_t step_size)
{
cfg->ssg = (SCIF_DFLL0SSG_EN
| SCIF_DFLL0SSG_AMPLITUDE(amplitude)
| SCIF_DFLL0SSG_STEPSIZE(step_size));
}
static inline void dfll_config_disable_ssg(struct dfll_config *cfg)
{
cfg->ssg = 0;
}
extern void dfll_enable_open_loop(const struct dfll_config *cfg,
uint32_t dfll_id);
extern void dfll_disable_open_loop(uint32_t dfll_id);
extern void dfll_enable_closed_loop(const struct dfll_config *cfg,
uint32_t dfll_id);
extern void dfll_disable_closed_loop(uint32_t dfll_id);
#ifndef CHIP_GENCLK_H_INCLUDED
// This function already has a prototype in genclk.h.
extern void dfll_enable_config_defaults(uint32_t dfll_id);
#endif
static inline bool dfll_is_coarse_locked(uint32_t dfll_id)
{
UNUSED(dfll_id);
return !!(SCIF->SCIF_PCLKSR & SCIF_PCLKSR_DFLL0LOCKC);
}
static inline bool dfll_is_fine_locked(uint32_t dfll_id)
{
UNUSED(dfll_id);
return !!(SCIF->SCIF_PCLKSR & SCIF_PCLKSR_DFLL0LOCKF);
}
static inline bool dfll_is_accurate_locked(uint32_t dfll_id)
{
UNUSED(dfll_id);
return (dfll_is_coarse_locked(dfll_id) &&
dfll_is_fine_locked(dfll_id));
}
static inline void dfll_enable_source(dfll_refclk_t src)
{
switch (src) {
case GENCLK_SRC_RCSYS:
/* Nothing to do */
break;
#ifdef BOARD_OSC32_HZ
case GENCLK_SRC_OSC32K:
if (!osc_is_ready(OSC_ID_OSC32)) {
osc_enable(OSC_ID_OSC32);
osc_wait_ready(OSC_ID_OSC32);
}
break;
#endif
#ifdef BOARD_OSC0_HZ
case GENCLK_SRC_OSC0:
if (!osc_is_ready(OSC_ID_OSC0)) {
osc_enable(OSC_ID_OSC0);
osc_wait_ready(OSC_ID_OSC0);
}
break;
#endif
case GENCLK_SRC_RC80M:
if (!osc_is_ready(OSC_ID_RC80M)) {
osc_enable(OSC_ID_RC80M);
osc_wait_ready(OSC_ID_RC80M);
}
break;
case GENCLK_SRC_RC32K:
if (!osc_is_ready(OSC_ID_RC32K)) {
osc_enable(OSC_ID_RC32K);
osc_wait_ready(OSC_ID_RC32K);
}
break;
default:
Assert(false);
break;
}
}
#endif /* __ASSEMBLY__ */
//! @}
#ifdef __cplusplus
}
#endif
#endif /* CHIP_DFLL_H_INCLUDED */