/** | |
* \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 */ |