soc: arm: atmel_sam: common: Add PMC API for clock management
Add functions to Atmel SAM SoC PMC API. This is an effort to hide most of the internal registers used in different SAM families. Signed-off-by: Pieter De Gendt <pieter.degendt@basalte.be>
This commit is contained in:
parent
04de43b1a1
commit
20f1f44120
1 changed files with 485 additions and 0 deletions
|
@ -1,5 +1,7 @@
|
|||
/*
|
||||
* Copyright (c) 2016 Piotr Mienkowski
|
||||
* Copyright (c) 2023 Basalte bv
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
|
@ -11,7 +13,11 @@
|
|||
#ifndef _ATMEL_SAM_SOC_PMC_H_
|
||||
#define _ATMEL_SAM_SOC_PMC_H_
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <zephyr/sys/__assert.h>
|
||||
#include <zephyr/sys/util.h>
|
||||
#include <zephyr/types.h>
|
||||
#include <soc.h>
|
||||
|
||||
/**
|
||||
* @brief Enable the clock of specified peripheral module.
|
||||
|
@ -35,4 +41,483 @@ void soc_pmc_peripheral_disable(uint32_t id);
|
|||
*/
|
||||
uint32_t soc_pmc_peripheral_is_enabled(uint32_t id);
|
||||
|
||||
#if !defined(CONFIG_SOC_SERIES_SAM4L)
|
||||
|
||||
enum soc_pmc_fast_rc_freq {
|
||||
#if defined(CKGR_MOR_MOSCRCF_4_MHz)
|
||||
SOC_PMC_FAST_RC_FREQ_4MHZ = CKGR_MOR_MOSCRCF_4_MHz,
|
||||
#endif
|
||||
#if defined(CKGR_MOR_MOSCRCF_8_MHz)
|
||||
SOC_PMC_FAST_RC_FREQ_8MHZ = CKGR_MOR_MOSCRCF_8_MHz,
|
||||
#endif
|
||||
#if defined(CKGR_MOR_MOSCRCF_12_MHz)
|
||||
SOC_PMC_FAST_RC_FREQ_12MHZ = CKGR_MOR_MOSCRCF_12_MHz,
|
||||
#endif
|
||||
};
|
||||
|
||||
enum soc_pmc_mck_src {
|
||||
#if defined(PMC_MCKR_CSS_SLOW_CLK)
|
||||
SOC_PMC_MCK_SRC_SLOW_CLK = PMC_MCKR_CSS_SLOW_CLK,
|
||||
#endif
|
||||
#if defined(PMC_MCKR_CSS_MAIN_CLK)
|
||||
SOC_PMC_MCK_SRC_MAIN_CLK = PMC_MCKR_CSS_MAIN_CLK,
|
||||
#endif
|
||||
#if defined(PMC_MCKR_CSS_PLLA_CLK)
|
||||
SOC_PMC_MCK_SRC_PLLA_CLK = PMC_MCKR_CSS_PLLA_CLK,
|
||||
#endif
|
||||
#if defined(PMC_MCKR_CSS_PLLB_CLK)
|
||||
SOC_PMC_MCK_SRC_PLLB_CLK = PMC_MCKR_CSS_PLLB_CLK,
|
||||
#endif
|
||||
#if defined(PMC_MCKR_CSS_UPLL_CLK)
|
||||
SOC_PMC_MCK_SRC_UPLL_CLK = PMC_MCKR_CSS_UPLL_CLK,
|
||||
#endif
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Set the prescaler of the Master clock.
|
||||
*
|
||||
* @param prescaler the prescaler value.
|
||||
*/
|
||||
static ALWAYS_INLINE void soc_pmc_mck_set_prescaler(uint32_t prescaler)
|
||||
{
|
||||
uint32_t reg_val;
|
||||
|
||||
switch (prescaler) {
|
||||
case 1:
|
||||
reg_val = PMC_MCKR_PRES_CLK_1;
|
||||
break;
|
||||
case 2:
|
||||
reg_val = PMC_MCKR_PRES_CLK_2;
|
||||
break;
|
||||
case 4:
|
||||
reg_val = PMC_MCKR_PRES_CLK_4;
|
||||
break;
|
||||
case 8:
|
||||
reg_val = PMC_MCKR_PRES_CLK_8;
|
||||
break;
|
||||
case 16:
|
||||
reg_val = PMC_MCKR_PRES_CLK_16;
|
||||
break;
|
||||
case 32:
|
||||
reg_val = PMC_MCKR_PRES_CLK_32;
|
||||
break;
|
||||
case 64:
|
||||
reg_val = PMC_MCKR_PRES_CLK_64;
|
||||
break;
|
||||
case 3:
|
||||
reg_val = PMC_MCKR_PRES_CLK_3;
|
||||
break;
|
||||
default:
|
||||
__ASSERT(false, "Invalid MCK prescaler");
|
||||
reg_val = PMC_MCKR_PRES_CLK_1;
|
||||
break;
|
||||
}
|
||||
|
||||
PMC->PMC_MCKR = (PMC->PMC_MCKR & (~PMC_MCKR_PRES_Msk)) | reg_val;
|
||||
|
||||
while (!(PMC->PMC_SR & PMC_SR_MCKRDY)) {
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(CONFIG_SOC_SERIES_SAME70) || defined(CONFIG_SOC_SERIES_SAMV71)
|
||||
|
||||
/**
|
||||
* @brief Set the divider of the Master clock.
|
||||
*
|
||||
* @param divider the divider value.
|
||||
*/
|
||||
static ALWAYS_INLINE void soc_pmc_mck_set_divider(uint32_t divider)
|
||||
{
|
||||
uint32_t reg_val;
|
||||
|
||||
switch (divider) {
|
||||
case 1:
|
||||
reg_val = PMC_MCKR_MDIV_EQ_PCK;
|
||||
break;
|
||||
case 2:
|
||||
reg_val = PMC_MCKR_MDIV_PCK_DIV2;
|
||||
break;
|
||||
case 3:
|
||||
reg_val = PMC_MCKR_MDIV_PCK_DIV3;
|
||||
break;
|
||||
case 4:
|
||||
reg_val = PMC_MCKR_MDIV_PCK_DIV4;
|
||||
break;
|
||||
default:
|
||||
__ASSERT(false, "Invalid MCK divider");
|
||||
reg_val = PMC_MCKR_MDIV_EQ_PCK;
|
||||
break;
|
||||
}
|
||||
|
||||
PMC->PMC_MCKR = (PMC->PMC_MCKR & (~PMC_MCKR_MDIV_Msk)) | reg_val;
|
||||
|
||||
while (!(PMC->PMC_SR & PMC_SR_MCKRDY)) {
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* CONFIG_SOC_SERIES_SAME70 || CONFIG_SOC_SERIES_SAMV71 */
|
||||
|
||||
/**
|
||||
* @brief Set the source of the Master clock.
|
||||
*
|
||||
* @param source the source identifier.
|
||||
*/
|
||||
static ALWAYS_INLINE void soc_pmc_mck_set_source(enum soc_pmc_mck_src source)
|
||||
{
|
||||
PMC->PMC_MCKR = (PMC->PMC_MCKR & (~PMC_MCKR_CSS_Msk)) | (uint32_t)source;
|
||||
|
||||
while (!(PMC->PMC_SR & PMC_SR_MCKRDY)) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Switch main clock source selection to internal fast RC.
|
||||
*
|
||||
* @param freq the internal fast RC desired frequency 4/8/12MHz.
|
||||
*/
|
||||
static ALWAYS_INLINE void soc_pmc_switch_mainck_to_fastrc(enum soc_pmc_fast_rc_freq freq)
|
||||
{
|
||||
/* Enable Fast RC oscillator but DO NOT switch to RC now */
|
||||
PMC->CKGR_MOR |= CKGR_MOR_KEY_PASSWD | CKGR_MOR_MOSCRCEN;
|
||||
|
||||
/* Wait for the Fast RC to stabilize */
|
||||
while (!(PMC->PMC_SR & PMC_SR_MOSCRCS)) {
|
||||
}
|
||||
|
||||
/* Change Fast RC oscillator frequency */
|
||||
PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCRCF_Msk)
|
||||
| CKGR_MOR_KEY_PASSWD
|
||||
| (uint32_t)freq;
|
||||
|
||||
/* Wait for the Fast RC to stabilize */
|
||||
while (!(PMC->PMC_SR & PMC_SR_MOSCRCS)) {
|
||||
}
|
||||
|
||||
/* Switch to Fast RC */
|
||||
PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCSEL)
|
||||
| CKGR_MOR_KEY_PASSWD;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable internal fast RC oscillator.
|
||||
*
|
||||
* @param freq the internal fast RC desired frequency 4/8/12MHz.
|
||||
*/
|
||||
static ALWAYS_INLINE void soc_pmc_osc_enable_fastrc(enum soc_pmc_fast_rc_freq freq)
|
||||
{
|
||||
/* Enable Fast RC oscillator but DO NOT switch to RC */
|
||||
PMC->CKGR_MOR |= CKGR_MOR_KEY_PASSWD | CKGR_MOR_MOSCRCEN;
|
||||
|
||||
/* Wait for the Fast RC to stabilize */
|
||||
while (!(PMC->PMC_SR & PMC_SR_MOSCRCS)) {
|
||||
}
|
||||
|
||||
/* Change Fast RC oscillator frequency */
|
||||
PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCRCF_Msk)
|
||||
| CKGR_MOR_KEY_PASSWD
|
||||
| (uint32_t)freq;
|
||||
|
||||
/* Wait for the Fast RC to stabilize */
|
||||
while (!(PMC->PMC_SR & PMC_SR_MOSCRCS)) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disable internal fast RC oscillator.
|
||||
*/
|
||||
static ALWAYS_INLINE void soc_pmc_osc_disable_fastrc(void)
|
||||
{
|
||||
/* Disable Fast RC oscillator */
|
||||
PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCRCEN & ~CKGR_MOR_MOSCRCF_Msk)
|
||||
| CKGR_MOR_KEY_PASSWD;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if the internal fast RC is ready.
|
||||
*
|
||||
* @return true if internal fast RC is ready, false otherwise
|
||||
*/
|
||||
static ALWAYS_INLINE bool soc_pmc_osc_is_ready_fastrc(void)
|
||||
{
|
||||
return (PMC->PMC_SR & PMC_SR_MOSCRCS);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable the external crystal oscillator.
|
||||
*
|
||||
* @param xtal_startup_time crystal start-up time, in number of slow clocks.
|
||||
*/
|
||||
static ALWAYS_INLINE void soc_pmc_osc_enable_main_xtal(uint32_t xtal_startup_time)
|
||||
{
|
||||
uint32_t mor = PMC->CKGR_MOR;
|
||||
|
||||
mor &= ~(CKGR_MOR_MOSCXTBY | CKGR_MOR_MOSCXTEN);
|
||||
mor |= CKGR_MOR_KEY_PASSWD | CKGR_MOR_MOSCXTEN | CKGR_MOR_MOSCXTST(xtal_startup_time);
|
||||
|
||||
PMC->CKGR_MOR = mor;
|
||||
|
||||
/* Wait the main Xtal to stabilize */
|
||||
while (!(PMC->PMC_SR & PMC_SR_MOSCXTS)) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Bypass the external crystal oscillator.
|
||||
*/
|
||||
static ALWAYS_INLINE void soc_pmc_osc_bypass_main_xtal(void)
|
||||
{
|
||||
uint32_t mor = PMC->CKGR_MOR;
|
||||
|
||||
mor &= ~(CKGR_MOR_MOSCXTBY | CKGR_MOR_MOSCXTEN);
|
||||
mor |= CKGR_MOR_KEY_PASSWD | CKGR_MOR_MOSCXTBY;
|
||||
|
||||
/* Enable Crystal oscillator but DO NOT switch now. Keep MOSCSEL to 0 */
|
||||
PMC->CKGR_MOR = mor;
|
||||
/* The MOSCXTS in PMC_SR is automatically set */
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disable the external crystal oscillator.
|
||||
*/
|
||||
static ALWAYS_INLINE void soc_pmc_osc_disable_main_xtal(void)
|
||||
{
|
||||
uint32_t mor = PMC->CKGR_MOR;
|
||||
|
||||
mor &= ~(CKGR_MOR_MOSCXTBY | CKGR_MOR_MOSCXTEN);
|
||||
|
||||
PMC->CKGR_MOR = CKGR_MOR_KEY_PASSWD | mor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if the external crystal oscillator is bypassed.
|
||||
*
|
||||
* @return true if external crystal oscillator is bypassed, false otherwise
|
||||
*/
|
||||
static ALWAYS_INLINE bool soc_pmc_osc_is_bypassed_main_xtal(void)
|
||||
{
|
||||
return (PMC->CKGR_MOR & CKGR_MOR_MOSCXTBY);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if the external crystal oscillator is ready.
|
||||
*
|
||||
* @return true if external crystal oscillator is ready, false otherwise
|
||||
*/
|
||||
static ALWAYS_INLINE bool soc_pmc_osc_is_ready_main_xtal(void)
|
||||
{
|
||||
return (PMC->PMC_SR & PMC_SR_MOSCXTS);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Switch main clock source selection to external crystal oscillator.
|
||||
*
|
||||
* @param bypass select bypass or xtal
|
||||
* @param xtal_startup_time crystal start-up time, in number of slow clocks
|
||||
*/
|
||||
static ALWAYS_INLINE void soc_pmc_switch_mainck_to_xtal(bool bypass, uint32_t xtal_startup_time)
|
||||
{
|
||||
soc_pmc_osc_enable_main_xtal(xtal_startup_time);
|
||||
|
||||
/* Enable Main Xtal oscillator */
|
||||
if (bypass) {
|
||||
PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCXTEN)
|
||||
| CKGR_MOR_KEY_PASSWD
|
||||
| CKGR_MOR_MOSCXTBY
|
||||
| CKGR_MOR_MOSCSEL;
|
||||
} else {
|
||||
PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCXTBY)
|
||||
| CKGR_MOR_KEY_PASSWD
|
||||
| CKGR_MOR_MOSCXTEN
|
||||
| CKGR_MOR_MOSCXTST(xtal_startup_time);
|
||||
|
||||
/* Wait for the Xtal to stabilize */
|
||||
while (!(PMC->PMC_SR & PMC_SR_MOSCXTS)) {
|
||||
}
|
||||
|
||||
PMC->CKGR_MOR |= CKGR_MOR_KEY_PASSWD | CKGR_MOR_MOSCSEL;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disable the external crystal oscillator.
|
||||
*
|
||||
* @param bypass select bypass or xtal
|
||||
*/
|
||||
static ALWAYS_INLINE void soc_pmc_osc_disable_xtal(bool bypass)
|
||||
{
|
||||
/* Disable xtal oscillator */
|
||||
if (bypass) {
|
||||
PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCXTBY)
|
||||
| CKGR_MOR_KEY_PASSWD;
|
||||
} else {
|
||||
PMC->CKGR_MOR = (PMC->CKGR_MOR & ~CKGR_MOR_MOSCXTEN)
|
||||
| CKGR_MOR_KEY_PASSWD;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if the main clock is ready. Depending on MOSCEL, main clock can be one
|
||||
* of external crystal, bypass or internal RC.
|
||||
*
|
||||
* @return true if main clock is ready, false otherwise
|
||||
*/
|
||||
static ALWAYS_INLINE bool soc_pmc_osc_is_ready_mainck(void)
|
||||
{
|
||||
return PMC->PMC_SR & PMC_SR_MOSCSELS;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable Wait Mode.
|
||||
*/
|
||||
static ALWAYS_INLINE void soc_pmc_enable_waitmode(void)
|
||||
{
|
||||
PMC->PMC_FSMR |= PMC_FSMR_LPM;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable Clock Failure Detector.
|
||||
*/
|
||||
static ALWAYS_INLINE void soc_pmc_enable_clock_failure_detector(void)
|
||||
{
|
||||
uint32_t mor = PMC->CKGR_MOR;
|
||||
|
||||
PMC->CKGR_MOR = CKGR_MOR_KEY_PASSWD | CKGR_MOR_CFDEN | mor;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disable Clock Failure Detector.
|
||||
*/
|
||||
static ALWAYS_INLINE void soc_pmc_disable_clock_failure_detector(void)
|
||||
{
|
||||
uint32_t mor = PMC->CKGR_MOR & (~CKGR_MOR_CFDEN);
|
||||
|
||||
PMC->CKGR_MOR = CKGR_MOR_KEY_PASSWD | mor;
|
||||
}
|
||||
|
||||
#if defined(PMC_MCKR_CSS_PLLA_CLK)
|
||||
|
||||
/**
|
||||
* @brief Disable the PLLA clock.
|
||||
*/
|
||||
static ALWAYS_INLINE void soc_pmc_disable_pllack(void)
|
||||
{
|
||||
PMC->CKGR_PLLAR = CKGR_PLLAR_ONE | CKGR_PLLAR_MULA(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable the PLLA clock.
|
||||
*
|
||||
* @param mula PLLA multiplier
|
||||
* @param pllacount PLLA lock counter, in number of slow clocks
|
||||
* @param diva PLLA Divider
|
||||
*/
|
||||
static ALWAYS_INLINE void soc_pmc_enable_pllack(uint32_t mula, uint32_t pllacount, uint32_t diva)
|
||||
{
|
||||
__ASSERT(diva > 0, "Invalid PLLA divider");
|
||||
|
||||
/* first disable the PLL to unlock the lock */
|
||||
soc_pmc_disable_pllack();
|
||||
|
||||
PMC->CKGR_PLLAR = CKGR_PLLAR_ONE
|
||||
| CKGR_PLLAR_DIVA(diva)
|
||||
| CKGR_PLLAR_PLLACOUNT(pllacount)
|
||||
| CKGR_PLLAR_MULA(mula);
|
||||
|
||||
while ((PMC->PMC_SR & PMC_SR_LOCKA) == 0) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if the PLLA is locked.
|
||||
*
|
||||
* @return true if PLLA is locked, false otherwise
|
||||
*/
|
||||
static ALWAYS_INLINE bool soc_pmc_is_locked_pllack(void)
|
||||
{
|
||||
return (PMC->PMC_SR & PMC_SR_LOCKA);
|
||||
}
|
||||
|
||||
#endif /* PMC_MCKR_CSS_PLLA_CLK */
|
||||
|
||||
#if defined(PMC_MCKR_CSS_PLLB_CLK)
|
||||
|
||||
/**
|
||||
* @brief Disable the PLLB clock.
|
||||
*/
|
||||
static ALWAYS_INLINE void soc_pmc_disable_pllbck(void)
|
||||
{
|
||||
PMC->CKGR_PLLBR = CKGR_PLLBR_MULB(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Enable the PLLB clock.
|
||||
*
|
||||
* @param mulb PLLB multiplier
|
||||
* @param pllbcount PLLB lock counter, in number of slow clocks
|
||||
* @param divb PLLB Divider
|
||||
*/
|
||||
static ALWAYS_INLINE void soc_pmc_enable_pllbck(uint32_t mulb, uint32_t pllbcount, uint32_t divb)
|
||||
{
|
||||
__ASSERT(divb > 0, "Invalid PLLB divider");
|
||||
|
||||
/* first disable the PLL to unlock the lock */
|
||||
soc_pmc_disable_pllbck();
|
||||
|
||||
PMC->CKGR_PLLBR = CKGR_PLLBR_DIVB(divb)
|
||||
| CKGR_PLLBR_PLLBCOUNT(pllbcount)
|
||||
| CKGR_PLLBR_MULB(mulb);
|
||||
|
||||
while ((PMC->PMC_SR & PMC_SR_LOCKB) == 0) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if the PLLB is locked.
|
||||
*
|
||||
* @return true if PLLB is locked, false otherwise
|
||||
*/
|
||||
static ALWAYS_INLINE bool soc_pmc_is_locked_pllbck(void)
|
||||
{
|
||||
return (PMC->PMC_SR & PMC_SR_LOCKB);
|
||||
}
|
||||
|
||||
#endif /* PMC_MCKR_CSS_PLLB_CLK */
|
||||
|
||||
#if defined(PMC_MCKR_CSS_UPLL_CLK)
|
||||
|
||||
/**
|
||||
* @brief Enable the UPLL clock.
|
||||
*/
|
||||
static ALWAYS_INLINE void soc_pmc_enable_upllck(uint32_t upllcount)
|
||||
{
|
||||
PMC->CKGR_UCKR = CKGR_UCKR_UPLLCOUNT(upllcount)
|
||||
| CKGR_UCKR_UPLLEN;
|
||||
|
||||
/* Wait UTMI PLL Lock Status */
|
||||
while (!(PMC->PMC_SR & PMC_SR_LOCKU)) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Disable the UPLL clock.
|
||||
*/
|
||||
static ALWAYS_INLINE void soc_pmc_disable_upllck(void)
|
||||
{
|
||||
PMC->CKGR_UCKR &= ~CKGR_UCKR_UPLLEN;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check if the UPLL is locked.
|
||||
*
|
||||
* @return true if UPLL is locked, false otherwise
|
||||
*/
|
||||
static ALWAYS_INLINE bool soc_pmc_is_locked_upllck(void)
|
||||
{
|
||||
return (PMC->PMC_SR & PMC_SR_LOCKU);
|
||||
}
|
||||
|
||||
#endif /* PMC_MCKR_CSS_UPLL_CLK */
|
||||
|
||||
#endif /* !CONFIG_SOC_SERIES_SAM4L */
|
||||
|
||||
#endif /* _ATMEL_SAM_SOC_PMC_H_ */
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue