/**
* \file
*
* \brief Chip-specific system clock management functions.
*
* Copyright (c) 2013-2018 Microchip Technology Inc. and its subsidiaries.
*
* \asf_license_start
*
* \page License
*
* Subject to your compliance with these terms, you may use Microchip
* software and any derivatives exclusively with Microchip products.
* It is your responsibility to comply with third party license terms applicable
* to your use of third party software (including open source software) that
* may accompany Microchip software.
*
* THIS SOFTWARE IS SUPPLIED BY MICROCHIP "AS IS". NO WARRANTIES,
* WHETHER EXPRESS, IMPLIED OR STATUTORY, APPLY TO THIS SOFTWARE,
* INCLUDING ANY IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY,
* AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT WILL MICROCHIP BE
* LIABLE FOR ANY INDIRECT, SPECIAL, PUNITIVE, INCIDENTAL OR CONSEQUENTIAL
* LOSS, DAMAGE, COST OR EXPENSE OF ANY KIND WHATSOEVER RELATED TO THE
* SOFTWARE, HOWEVER CAUSED, EVEN IF MICROCHIP HAS BEEN ADVISED OF THE
* POSSIBILITY OR THE DAMAGES ARE FORESEEABLE. TO THE FULLEST EXTENT
* ALLOWED BY LAW, MICROCHIP'S TOTAL LIABILITY ON ALL CLAIMS IN ANY WAY
* RELATED TO THIS SOFTWARE WILL NOT EXCEED THE AMOUNT OF FEES, IF ANY,
* THAT YOU HAVE PAID DIRECTLY TO MICROCHIP FOR THIS SOFTWARE.
*
* \asf_license_stop
*
*/
/*
* Support and FAQ: visit Microchip Support
*/
#include
#include
#include
/// @cond 0
/**INDENT-OFF**/
#ifdef __cplusplus
extern "C" {
#endif
/**INDENT-ON**/
/// @endcond
/**
* \weakgroup sysclk_group
* @{
*/
#if defined(CONFIG_SYSCLK_DEFAULT_RETURNS_SLOW_OSC)
/**
* \brief boolean signalling that the sysclk_init is done.
*/
uint32_t sysclk_initialized = 0;
#endif
/**
* \brief Set system clock prescaler configuration
*
* This function will change the system clock prescaler configuration to
* match the parameters.
*
* \note The parameters to this function are device-specific.
*
* \param cpu_shift The CPU clock will be divided by \f$2^{mck\_pres}\f$
*/
void sysclk_set_prescalers(uint32_t ul_pres)
{
pmc_mck_set_prescaler(ul_pres);
SystemCoreClockUpdate();
}
/**
* \brief Change the source of the main system clock.
*
* \param src The new system clock source. Must be one of the constants
* from the System Clock Sources section.
*/
void sysclk_set_source(uint32_t ul_src)
{
switch (ul_src) {
case SYSCLK_SRC_SLCK_RC:
case SYSCLK_SRC_SLCK_XTAL:
case SYSCLK_SRC_SLCK_BYPASS:
pmc_mck_set_source(PMC_MCKR_CSS_SLOW_CLK);
break;
case SYSCLK_SRC_MAINCK_8M_RC:
case SYSCLK_SRC_MAINCK_16M_RC:
case SYSCLK_SRC_MAINCK_24M_RC:
case SYSCLK_SRC_MAINCK_XTAL:
case SYSCLK_SRC_MAINCK_BYPASS:
pmc_mck_set_source(PMC_MCKR_CSS_MAIN_CLK);
break;
case SYSCLK_SRC_PLLACK:
pmc_mck_set_source(PMC_MCKR_CSS_PLLA_CLK);
break;
#if SAMG55
case SYSCLK_SRC_PLLBCK:
pmc_mck_set_source(PMC_MCKR_CSS_PLLB_CLK);
break;
#endif
}
SystemCoreClockUpdate();
}
#if SAMG55
#if defined(CONFIG_USBCLK_SOURCE) || defined(__DOXYGEN__)
/**
* \brief Enable USB clock.
*
*
* \param pll_id Source of the USB clock.
* \param div Actual clock divisor. Must be superior to 0.
*/
void sysclk_enable_usb(void)
{
Assert(CONFIG_USBCLK_DIV > 0);
#ifdef CONFIG_PLL0_SOURCE
if (CONFIG_USBCLK_SOURCE == USBCLK_SRC_PLL0) {
struct pll_config pllcfg;
pll_enable_source(CONFIG_PLL0_SOURCE);
pll_config_defaults(&pllcfg, 0);
pll_enable(&pllcfg, 0);
pll_wait_for_lock(0);
#ifdef UHD_ENABLE
pmc_switch_uhpck_to_pllack(CONFIG_USBCLK_DIV - 1);
pmc_enable_uhpck();
#else
pmc_switch_udpck_to_pllack(CONFIG_USBCLK_DIV - 1);
pmc_enable_udpck();
#endif
return;
}
#endif
#ifdef CONFIG_PLL1_SOURCE
if (CONFIG_USBCLK_SOURCE == USBCLK_SRC_PLL1) {
struct pll_config pllcfg;
pll_enable_source(CONFIG_PLL1_SOURCE);
pll_config_defaults(&pllcfg, 1);
pll_enable(&pllcfg, 1);
pll_wait_for_lock(1);
#ifdef UHD_ENABLE
pmc_switch_uhpck_to_pllbck(CONFIG_USBCLK_DIV - 1);
pmc_enable_uhpck();
#else
pmc_switch_udpck_to_pllbck(CONFIG_USBCLK_DIV - 1);
pmc_enable_udpck();
#endif
return;
}
#endif
}
/**
* \brief Disable the USB clock.
*
* \note This implementation does not switch off the PLL, it just turns off the USB clock.
*/
void sysclk_disable_usb(void)
{
pmc_disable_udpck();
}
#endif // CONFIG_USBCLK_SOURCE
#endif
void sysclk_init(void)
{
#if (SAMG54 || SAMG55)
uint32_t unique_id[32];
uint32_t trim_value;
#endif
/* Set flash wait state to max in case the below clock switching. */
system_init_flash(CHIP_FREQ_CPU_MAX);
/* Config system clock setting */
if (CONFIG_SYSCLK_SOURCE == SYSCLK_SRC_SLCK_RC) {
osc_enable(OSC_SLCK_32K_RC);
osc_wait_ready(OSC_SLCK_32K_RC);
pmc_switch_mck_to_sclk(CONFIG_SYSCLK_PRES);
}
else if (CONFIG_SYSCLK_SOURCE == SYSCLK_SRC_SLCK_XTAL) {
osc_enable(OSC_SLCK_32K_XTAL);
osc_wait_ready(OSC_SLCK_32K_XTAL);
pmc_switch_mck_to_sclk(CONFIG_SYSCLK_PRES);
}
else if (CONFIG_SYSCLK_SOURCE == SYSCLK_SRC_SLCK_BYPASS) {
osc_enable(OSC_SLCK_32K_BYPASS);
osc_wait_ready(OSC_SLCK_32K_BYPASS);
pmc_switch_mck_to_sclk(CONFIG_SYSCLK_PRES);
}
else if (CONFIG_SYSCLK_SOURCE == SYSCLK_SRC_MAINCK_8M_RC) {
/* Already running from SYSCLK_SRC_MAINCK_8M_RC */
}
else if (CONFIG_SYSCLK_SOURCE == SYSCLK_SRC_MAINCK_16M_RC) {
osc_enable(OSC_MAINCK_16M_RC);
osc_wait_ready(OSC_MAINCK_16M_RC);
pmc_switch_mck_to_mainck(CONFIG_SYSCLK_PRES);
}
else if (CONFIG_SYSCLK_SOURCE == SYSCLK_SRC_MAINCK_24M_RC) {
osc_enable(OSC_MAINCK_24M_RC);
osc_wait_ready(OSC_MAINCK_24M_RC);
pmc_switch_mck_to_mainck(CONFIG_SYSCLK_PRES);
}
else if (CONFIG_SYSCLK_SOURCE == SYSCLK_SRC_MAINCK_XTAL) {
osc_enable(OSC_MAINCK_XTAL);
osc_wait_ready(OSC_MAINCK_XTAL);
pmc_switch_mck_to_mainck(CONFIG_SYSCLK_PRES);
}
else if (CONFIG_SYSCLK_SOURCE == SYSCLK_SRC_MAINCK_BYPASS) {
osc_enable(OSC_MAINCK_BYPASS);
osc_wait_ready(OSC_MAINCK_BYPASS);
pmc_switch_mck_to_mainck(CONFIG_SYSCLK_PRES);
}
#ifdef CONFIG_PLL0_SOURCE
else if (CONFIG_SYSCLK_SOURCE == SYSCLK_SRC_PLLACK) {
struct pll_config pllcfg;
pll_enable_source(CONFIG_PLL0_SOURCE);
pll_config_defaults(&pllcfg, 0);
pll_enable(&pllcfg, 0);
pll_wait_for_lock(0);
pmc_switch_mck_to_pllack(CONFIG_SYSCLK_PRES);
}
#endif
#if SAMG55
#ifdef CONFIG_PLL1_SOURCE
else if (CONFIG_SYSCLK_SOURCE == SYSCLK_SRC_PLLBCK) {
struct pll_config pllcfg;
pll_enable_source(CONFIG_PLL1_SOURCE);
pll_config_defaults(&pllcfg, 1);
pll_enable(&pllcfg, 1);
pll_wait_for_lock(1);
pmc_switch_mck_to_pllbck(CONFIG_SYSCLK_PRES);
}
#endif
#endif
/* Update the SystemFrequency variable */
SystemCoreClockUpdate();
/* Set a flash wait state depending on the new cpu frequency */
system_init_flash(sysclk_get_cpu_hz());
#if SAMG54
/* Set the trim value when system run near 96M */
if ((SystemCoreClock <= (CHIP_FREQ_CPU_MAX + (CHIP_FREQ_CPU_MAX >> 3))) &&
(SystemCoreClock >= (CHIP_FREQ_CPU_MAX - (CHIP_FREQ_CPU_MAX >> 3)))) {
/* Get the trim value from unique ID area */
efc_perform_read_sequence(EFC, EFC_FCMD_STUI, EFC_FCMD_SPUI,
unique_id, 32);
#ifdef BOARD_VDDIO_18
trim_value = unique_id[10] & 0x000000FF;
supc_set_regulator_trim_user(SUPC, trim_value);
#else
trim_value = unique_id[12] & 0x00FF0000;
trim_value = trim_value >> 16;
supc_set_regulator_trim_user(SUPC, trim_value);
#endif
}
#endif
#if SAMG55
/* Set the trim value when system run near 120M */
if ((SystemCoreClock <= (CHIP_FREQ_CPU_MAX + (CHIP_FREQ_CPU_MAX >> 3))) &&
(SystemCoreClock >= (CHIP_FREQ_CPU_MAX - (CHIP_FREQ_CPU_MAX >> 3)))) {
/* Get the trim value from unique ID area */
efc_perform_read_sequence(EFC, EFC_FCMD_STUI, EFC_FCMD_SPUI,
unique_id, 32);
trim_value = unique_id[16] & 0x0000FFFF;
supc_set_regulator_trim_user(SUPC, trim_value);
}
#endif
#if (defined CONFIG_SYSCLK_DEFAULT_RETURNS_SLOW_OSC)
/* Signal that the internal frequencies are setup */
sysclk_initialized = 1;
#endif
}
//! @}
/// @cond 0
/**INDENT-OFF**/
#ifdef __cplusplus
}
#endif
/**INDENT-ON**/
/// @endcond