zephyr/drivers/clock_control/clock_stm32_ll_wb0.c

793 lines
22 KiB
C

/*
* Copyright (c) 2024 STMicroelectronics
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <soc.h>
#include <stm32_ll_bus.h>
#include <stm32_ll_pwr.h>
#include <stm32_ll_rcc.h>
#include <stm32_ll_radio.h>
#include <stm32_ll_system.h>
#include <stm32_ll_utils.h>
#include <zephyr/irq.h>
#include <zephyr/kernel.h>
#include <zephyr/sys/util.h>
#include <zephyr/toolchain.h>
#include <zephyr/sys/__assert.h>
#include <zephyr/arch/common/sys_io.h>
#include <zephyr/arch/common/sys_bitops.h>
#include <zephyr/drivers/clock_control/stm32_clock_control.h>
/* Driver definitions */
#define RCC_REG(_reg_offset) (DT_REG_ADDR(STM32_CLOCK_CONTROL_NODE) + (_reg_offset))
#define RADIO_CTRL_IRQn 21 /* Not provided by CMSIS; must be declared manually */
#define CLOCK_FREQ_64MHZ (64000000U)
#define CLOCK_FREQ_32MHZ (32000000U)
#define CLOCK_FREQ_16MHZ (16000000U)
/* Device tree node definitions */
#define DT_RCC_SLOWCLK_NODE DT_PHANDLE(STM32_CLOCK_CONTROL_NODE, slow_clock)
#define DT_LSI_NODE DT_NODELABEL(clk_lsi)
/* Device tree properties definitions */
#define STM32_WB0_CLKSYS_PRESCALER \
DT_PROP(STM32_CLOCK_CONTROL_NODE, clksys_prescaler)
#if DT_NODE_HAS_PROP(STM32_CLOCK_CONTROL_NODE, slow_clock)
# if !DT_NODE_HAS_STATUS_OKAY(DT_RCC_SLOWCLK_NODE)
# error slow-clock source is not enabled
# endif
# if DT_SAME_NODE(DT_RCC_SLOWCLK_NODE, DT_LSI_NODE)
# define STM32_WB0_SLOWCLK_SRC LL_RCC_LSCO_CLKSOURCE_LSI
# elif DT_SAME_NODE(DT_RCC_SLOWCLK_NODE, DT_NODELABEL(clk_lse))
# define STM32_WB0_SLOWCLK_SRC LL_RCC_LSCO_CLKSOURCE_LSE
# elif DT_SAME_NODE(DT_RCC_SLOWCLK_NODE, DT_NODELABEL(clk_16mhz_div512))
# define STM32_WB0_SLOWCLK_SRC LL_RCC_LSCO_CLKSOURCE_HSI64M_DIV2048
# else
# error Invalid device selected as slow-clock
# endif
#endif /* DT_NODE_HAS_PROP(STM32_CLOCK_CONTROL_NODE, slow_clock) */
#if DT_NODE_HAS_PROP(DT_LSI_NODE, runtime_measurement_interval)
#define STM32_WB0_RUNTIME_LSI_CALIBRATION 1
#define STM32_WB0_LSI_RUNTIME_CALIB_INTERVAL \
DT_PROP(DT_LSI_NODE, runtime_measurement_interval)
#endif /* DT_NODE_HAS_PROP(clk_lsi, runtime_calibration_settings) */
/* Verify device tree properties are correct */
BUILD_ASSERT(!IS_ENABLED(STM32_SYSCLK_SRC_HSE) || STM32_WB0_CLKSYS_PRESCALER != 64,
"clksys-prescaler cannot be 64 when SYSCLK source is Direct HSE");
#if defined(STM32_LSI_ENABLED)
/* Check clock configuration allows MR_BLE IP to work.
* This IP is required to perform LSI measurements.
*/
# if defined(STM32_SYSCLK_SRC_HSI)
/* When using HSI without PLL, the "16MHz" output is not actually 16MHz, since
* the RC64M generator is imprecise. In this configuration, MR_BLE is broken.
* The CPU and MR_BLE must be running at 32MHz for MR_BLE to work with HSI.
*/
BUILD_ASSERT(CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC >= CLOCK_FREQ_32MHZ,
"System clock frequency must be at least 32MHz to use LSI");
# else
/* In PLL or Direct HSE mode, the clock is stable, so 16MHz can be used. */
BUILD_ASSERT(CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC >= CLOCK_FREQ_16MHZ,
"System clock frequency must be at least 16MHz to use LSI");
# endif /* STM32_SYSCLK_SRC_HSI */
/**
* @brief Variable holding the "current frequency of LSI", according
* to the measurement process. This variable is updated each time
* a new measurement of the LSI frequency is performed.
*/
static volatile uint32_t stm32wb0_lsi_frequency = STM32_LSI_FREQ;
/**
* @brief Perform a measurement of the LSI frequency and updates
* the @p stm32wb0_lsi_frequency global variable based on the results.
*
* @param wait_event Semaphore to wait for completion of the measurement
* If NULL, RADIO_CONTROL registers are polled instead.
*/
static void measure_lsi_frequency(struct k_sem *wait_event)
{
uint32_t fast_clock_cycles_elapsed;
/* Ensure calibration flag is clear */
LL_RADIO_TIMER_ClearFlag_LSICalibrationEnded(RADIO_CTRL);
/* Setup the calibration parameters
*
* NOTE: (size - 1) is required to get the correct count,
* because the value in the register is one less than the
* actual number of periods requested for calibration.
*/
LL_RADIO_TIMER_SetLSIWindowCalibrationLength(RADIO_CTRL,
(CONFIG_STM32WB0_LSI_MEASUREMENT_WINDOW - 1));
/* Start LSI calibration */
LL_RADIO_TIMER_StartLSICalibration(RADIO_CTRL);
if (wait_event) {
/* Wait for semaphore to be signaled */
k_sem_take(wait_event, K_FOREVER);
} else {
while (!LL_RADIO_TIMER_IsActiveFlag_LSICalibrationEnded(RADIO_CTRL)) {
/* Wait for calibration to finish (polling) */
}
/* Clear calibration complete flag / interrupt */
LL_RADIO_TIMER_ClearFlag_LSICalibrationEnded(RADIO_CTRL);
}
/* Read calibration results */
fast_clock_cycles_elapsed = LL_RADIO_TIMER_GetLSIPeriod(RADIO_CTRL);
/**
* Calculate LSI frequency from calibration results and update
* the corresponding global variable
*
* LSI calibration counts the amount of 16MHz clock half-periods that
* occur until a certain amount of slow clock cycles have been observed.
*
* @p fast_clock_cycles_elapsed is the number of 16MHz clock half-periods
* elapsed while waiting for @p STM32_WB0_LSI_MEASURE_WINDOW_SIZE LSI periods
* to occur. The LSI frequency can be calculated the following way:
*
* t = <number of periods counted> / <clock frequency>
*
* ==> Time taken for calibration:
*
* tCALIB = @p fast_clock_cycles_elapsed / (2 * 16MHz)
*
* ==> LSI period:
*
* tLSI = tCALIB / @p STM32_WB0_LSI_MEASURE_WINDOW_SIZE
*
* ( @p fast_clock_cycles_elapsed / (2 * 16MHz) )
* = ------------------------------------------------
* @p STM32_WB0_LSI_MEASURE_WINDOW_SIZE
*
* ==> LSI frequency:
*
* fLSI = (1 / tLSI)
*
* @p STM32_WB0_LSI_MEASURE_WINDOW_SIZE
* = ------------------------------------------------
* ( @p fast_clock_cycles_elapsed / (2 * 16MHz) )
*
* (2 * 16MHz) * @p STM32_WB0_LSI_MEASURE_WINDOW_SIZE
* = -----------------------------------------------------
* @p fast_clock_cycles_elapsed
*
* NOTE: The division must be performed first to avoid 32-bit overflow.
*/
stm32wb0_lsi_frequency =
(CLOCK_FREQ_32MHZ / fast_clock_cycles_elapsed)
* CONFIG_STM32WB0_LSI_MEASUREMENT_WINDOW;
}
#endif /* STM32_LSI_ENABLED */
/** @brief Verifies if provided domain / bus clock is currently active */
int enabled_clock(uint32_t src_clk)
{
int r = 0;
switch (src_clk) {
case STM32_SRC_SYSCLK:
break;
case STM32_SRC_LSE:
if (!IS_ENABLED(STM32_LSE_ENABLED)) {
r = -ENOTSUP;
}
break;
case STM32_SRC_LSI:
if (!IS_ENABLED(STM32_LSI_ENABLED)) {
r = -ENOTSUP;
}
break;
case STM32_SRC_CLKSLOWMUX:
break;
case STM32_SRC_CLK16MHZ:
break;
case STM32_SRC_CLK32MHZ:
break;
default:
return -ENOTSUP;
}
return r;
}
static inline int stm32_clock_control_on(const struct device *dev,
clock_control_subsys_t sub_system)
{
struct stm32_pclken *pclken = (struct stm32_pclken *)(sub_system);
const mem_addr_t reg = RCC_REG(pclken->bus);
volatile uint32_t temp;
ARG_UNUSED(dev);
if (!IN_RANGE(pclken->bus, STM32_PERIPH_BUS_MIN, STM32_PERIPH_BUS_MAX)) {
/* Attempting to change domain clock */
return -ENOTSUP;
}
sys_set_bits(reg, pclken->enr);
/* Read back register to be blocked by RCC
* until peripheral clock enabling is complete
*/
temp = sys_read32(reg);
UNUSED(temp);
return 0;
}
static inline int stm32_clock_control_off(const struct device *dev,
clock_control_subsys_t sub_system)
{
struct stm32_pclken *pclken = (struct stm32_pclken *)(sub_system);
const mem_addr_t reg = RCC_REG(pclken->bus);
ARG_UNUSED(dev);
if (!IN_RANGE(pclken->bus, STM32_PERIPH_BUS_MIN, STM32_PERIPH_BUS_MAX)) {
/* Attempting to change domain clock */
return -ENOTSUP;
}
sys_clear_bits(reg, pclken->enr);
return 0;
}
static inline int stm32_clock_control_configure(const struct device *dev,
clock_control_subsys_t sub_system,
void *data)
{
struct stm32_pclken *pclken = (struct stm32_pclken *)sub_system;
const uint32_t shift = STM32_CLOCK_SHIFT_GET(pclken->enr);
mem_addr_t reg = RCC_REG(STM32_CLOCK_REG_GET(pclken->enr));
int err;
ARG_UNUSED(dev);
ARG_UNUSED(data);
err = enabled_clock(pclken->bus);
if (err < 0) {
/* Attempting to configure an unavailable or invalid clock */
return err;
}
sys_clear_bits(reg, STM32_CLOCK_MASK_GET(pclken->enr) << shift);
sys_set_bits(reg, STM32_CLOCK_VAL_GET(pclken->enr) << shift);
return 0;
}
static inline int get_apb0_periph_clkrate(uint32_t enr, uint32_t *rate,
uint32_t slow_clock, uint32_t sysclk, uint32_t clk_sys)
{
switch (enr) {
/* Slow clock peripherals: RTC & IWDG */
case LL_APB0_GRP1_PERIPH_RTC:
case LL_APB0_GRP1_PERIPH_WDG:
*rate = slow_clock;
break;
/* SYSCLK peripherals: all timers */
#if defined(TIM1)
case LL_APB0_GRP1_PERIPH_TIM1:
#endif
#if defined(TIM2)
case LL_APB0_GRP1_PERIPH_TIM2:
#endif
#if defined(TIM16)
case LL_APB0_GRP1_PERIPH_TIM16:
#endif
#if defined(TIM17)
case LL_APB0_GRP1_PERIPH_TIM17:
#endif
*rate = sysclk;
break;
/* CLK_SYS peripherals: SYSCFG */
case LL_APB0_GRP1_PERIPH_SYSCFG:
*rate = clk_sys;
break;
default:
return -ENOTSUP;
}
return 0;
}
static inline int get_apb1_periph_clkrate(uint32_t enr, uint32_t *rate,
uint32_t clk_sys)
{
switch (enr) {
#if defined(SPI1)
case LL_APB1_GRP1_PERIPH_SPI1:
*rate = clk_sys;
break;
#endif
#if defined(SPI2)
case LL_APB1_GRP1_PERIPH_SPI2:
switch (LL_RCC_GetSPI2I2SClockSource()) {
case LL_RCC_SPI2_I2S_CLK16M:
*rate = CLOCK_FREQ_16MHZ;
break;
case LL_RCC_SPI2_I2S_CLK32M:
*rate = CLOCK_FREQ_32MHZ;
break;
default:
CODE_UNREACHABLE;
}
break;
#endif
case LL_APB1_GRP1_PERIPH_SPI3:
switch (LL_RCC_GetSPI3I2SClockSource()) {
case LL_RCC_SPI3_I2S_CLK16M:
*rate = CLOCK_FREQ_16MHZ;
break;
case LL_RCC_SPI3_I2S_CLK32M:
*rate = CLOCK_FREQ_32MHZ;
break;
#if defined(LL_RCC_SPI3_I2S_CLK64M)
case LL_RCC_SPI3_I2S_CLK64M:
*rate = CLOCK_FREQ_64MHZ;
break;
#endif
default:
CODE_UNREACHABLE;
}
break;
case LL_APB1_GRP1_PERIPH_I2C1:
#if defined(I2C2)
case LL_APB1_GRP1_PERIPH_I2C2:
#endif
*rate = CLOCK_FREQ_16MHZ;
break;
case LL_APB1_GRP1_PERIPH_ADCDIG:
case LL_APB1_GRP1_PERIPH_ADCANA:
case (LL_APB1_GRP1_PERIPH_ADCDIG | LL_APB1_GRP1_PERIPH_ADCANA):
/* ADC has two enable bits - accept all combinations. */
*rate = CLOCK_FREQ_16MHZ;
break;
case LL_APB1_GRP1_PERIPH_USART1:
*rate = CLOCK_FREQ_16MHZ;
break;
case LL_APB1_GRP1_PERIPH_LPUART1:
#if !defined(RCC_CFGR_LPUCLKSEL)
*rate = CLOCK_FREQ_16MHZ;
#else
switch (LL_RCC_GetLPUARTClockSource()) {
case LL_RCC_LPUCLKSEL_CLK16M:
*rate = CLOCK_FREQ_16MHZ;
break;
case LL_RCC_LPUCLKSEL_CLKLSE:
*rate = STM32_LSE_FREQ;
break;
default:
CODE_UNREACHABLE;
}
#endif
break;
default:
return -ENOTSUP;
}
return 0;
}
static int stm32_clock_control_get_subsys_rate(const struct device *dev,
clock_control_subsys_t sub_system,
uint32_t *rate)
{
struct stm32_pclken *pclken = (struct stm32_pclken *)(sub_system);
uint32_t sysclk, slow_clock, clk_sys;
#if defined(STM32_LSI_ENABLED)
const uint32_t clk_lsi = stm32wb0_lsi_frequency;
#else
const uint32_t clk_lsi = 0;
#endif
ARG_UNUSED(dev);
/* Obtain SYSCLK frequency by checking which source drives high-speed clock tree.
* If Direct HSE is enabled, the high-speed tree is clocked by HSE @ 32MHz.
* Otherwise, the high-speed tree is clocked by the RC64MPLL clock @ 64MHz.
*
* NOTE: it is NOT possible to use the usual 'SystemCoreClock * Prescaler' approach on
* STM32WB0 because the prescaler configuration is not affected by input clock variation:
* setting CLKSYSDIV = 1 results in 32MHz CLK_SYS, regardless of SYSCLK being 32 or 64MHZ.
*/
if (LL_RCC_DIRECT_HSE_IsEnabled()) {
sysclk = STM32_HSE_FREQ;
} else {
sysclk = STM32_HSI_FREQ;
}
/* Obtain CLK_SYS (AHB0) frequency by using the CLKSYSDIV prescaler value.
*
* NOTE: LL_RCC_GetRC64MPLLPrescaler is strictly identical to LL_RCC_GetDirectHSEPrescaler
* and can be used regardless of which source is driving the high-speed clock tree.
*
* NOTE: the prescaler value must be interpreted as if source clock is 64MHz, regardless
* of which source is actually driving the high-speed clock tree. This allows using the
* following formula for calculations.
*
* NOTE: (x >> y) is equivalent to (x / 2^y) or (x / (1 << y)).
*/
clk_sys = CLOCK_FREQ_64MHZ >> LL_RCC_GetRC64MPLLPrescaler();
/* Obtain slow clock tree source by reading RCC_CFGR->LCOSEL.
* From this, we can deduce at which frequency the slow clock tree is running.
*/
switch (LL_RCC_LSCO_GetSource()) {
case LL_RCC_LSCO_CLKSOURCE_LSE:
slow_clock = STM32_LSE_FREQ;
break;
case LL_RCC_LSCO_CLKSOURCE_LSI:
slow_clock = clk_lsi;
break;
case LL_RCC_LSCO_CLKSOURCE_HSI64M_DIV2048:
slow_clock = CLOCK_FREQ_64MHZ / 2048;
break;
default:
__ASSERT(0, "Illegal slow clock source!");
CODE_UNREACHABLE;
}
switch (pclken->bus) {
case STM32_CLOCK_BUS_AHB0:
/* All peripherals on AHB0 are clocked by CLK_SYS. */
*rate = clk_sys;
break;
case STM32_CLOCK_BUS_APB0:
return get_apb0_periph_clkrate(pclken->enr, rate,
slow_clock, sysclk, clk_sys);
case STM32_CLOCK_BUS_APB1:
return get_apb1_periph_clkrate(pclken->enr, rate,
clk_sys);
case STM32_SRC_SYSCLK:
*rate = sysclk;
break;
case STM32_SRC_LSE:
*rate = STM32_LSE_FREQ;
break;
case STM32_SRC_LSI:
*rate = clk_lsi;
break;
case STM32_SRC_CLKSLOWMUX:
*rate = slow_clock;
break;
case STM32_SRC_CLK16MHZ:
*rate = CLOCK_FREQ_16MHZ;
break;
case STM32_SRC_CLK32MHZ:
*rate = CLOCK_FREQ_32MHZ;
break;
default:
case STM32_CLOCK_BUS_APB2:
/* The only periperhal on APB2 is the MR_BLE radio. However,
* it is clocked by two sources that run at different frequencies,
* and we are unable to determine which one this API's caller cares
* about. For this reason, return ENOTSUP anyways.
*
* Note that since the only driver that might call this API is the
* Bluetooth driver, and since it can already determine both frequencies
* very easily, this should not pose any problem.
*/
return -ENOTSUP;
}
return 0;
}
static enum clock_control_status stm32_clock_control_get_status(const struct device *dev,
clock_control_subsys_t sub_system)
{
struct stm32_pclken *pclken = (struct stm32_pclken *)sub_system;
ARG_UNUSED(dev);
if (IN_RANGE(pclken->bus, STM32_PERIPH_BUS_MIN, STM32_PERIPH_BUS_MAX)) {
/* Bus / gated clock */
if ((sys_read32(RCC_REG(pclken->bus)) & pclken->enr) == pclken->enr) {
return CLOCK_CONTROL_STATUS_ON;
} else {
return CLOCK_CONTROL_STATUS_OFF;
}
} else {
/* Domain clock */
if (enabled_clock(pclken->bus) == 0) {
return CLOCK_CONTROL_STATUS_ON;
} else {
return CLOCK_CONTROL_STATUS_OFF;
}
}
}
static struct clock_control_driver_api stm32_clock_control_api = {
.on = stm32_clock_control_on,
.off = stm32_clock_control_off,
.get_rate = stm32_clock_control_get_subsys_rate,
.get_status = stm32_clock_control_get_status,
.configure = stm32_clock_control_configure,
};
static void set_up_fixed_clock_sources(void)
{
if (IS_ENABLED(STM32_HSE_ENABLED)) {
/* Enable HSE */
LL_RCC_HSE_Enable();
while (LL_RCC_HSE_IsReady() != 1) {
/* Wait for HSE ready */
}
}
if (IS_ENABLED(STM32_HSI_ENABLED)) {
/* Enable HSI if not enabled */
if (LL_RCC_HSI_IsReady() != 1) {
/* Enable HSI */
LL_RCC_HSI_Enable();
while (LL_RCC_HSI_IsReady() != 1) {
/* Wait for HSI ready */
}
}
}
if (IS_ENABLED(STM32_LSI_ENABLED)) {
LL_RCC_LSI_Enable();
while (LL_RCC_LSI_IsReady() != 1) {
/* Wait for LSI ready */
}
}
if (IS_ENABLED(STM32_LSE_ENABLED)) {
#if STM32_LSE_DRIVING
/* Configure driving capability */
LL_RCC_LSE_SetDriveCapability(STM32_LSE_DRIVING << RCC_CSSWCR_LSEDRV_Pos);
#endif
/* Unconditionally disable pull-up & pull-down on LSE pins */
LL_PWR_SetNoPullB(LL_PWR_GPIO_BIT_12 | LL_PWR_GPIO_BIT_13);
if (IS_ENABLED(STM32_LSE_BYPASS)) {
/* Configure LSE bypass */
LL_RCC_LSE_EnableBypass();
}
/* Enable LSE Oscillator (32.768 kHz) */
LL_RCC_LSE_Enable();
while (!LL_RCC_LSE_IsReady()) {
/* Wait for LSE ready */
}
}
}
/* The STM32WB0 prescaler division factor defines vary depending on
* whether SYSCLK runs at 32MHz (Direct HSE) or 64MHz (RC64MPLL).
* The following helper macro wraps this difference.
*/
#if defined(STM32_SYSCLK_SRC_HSE)
#define LL_PRESCALER(x) LL_RCC_DIRECT_HSE_DIV_ ##x
#else
#define LL_PRESCALER(x) LL_RCC_RC64MPLL_DIV_ ##x
#endif
/**
* @brief Converts the Kconfig STM32_WB0_CLKSYS_PRESCALER option
* to a LL_RCC_RC64MPLL_DIV_x value understandable by the LL.
*/
static inline uint32_t kconfig_to_ll_prescaler(uint32_t kcfg_pre)
{
switch (kcfg_pre) {
case 1:
return LL_PRESCALER(1);
case 2:
return LL_PRESCALER(2);
case 4:
return LL_PRESCALER(4);
case 8:
return LL_PRESCALER(8);
case 16:
return LL_PRESCALER(16);
case 32:
return LL_PRESCALER(32);
#if !defined(STM32_SYSCLK_SRC_HSE)
/* A prescaler value of 64 is only valid when running
* off RC64MPLL because CLK_SYS must be at least 1MHz
*/
case 64:
return LL_RCC_RC64MPLL_DIV_64;
#endif
}
CODE_UNREACHABLE;
}
#undef LL_PRESCALER /* Undefine helper macro */
#if CONFIG_STM32WB0_LSI_RUNTIME_MEASUREMENT_INTERVAL != 0
K_SEM_DEFINE(lsi_measurement_sema, 0, 1);
#define NUM_SLOW_CLOCK_PERIPHERALS 3
/**
* Reserve one slot for each slow clock peripheral to ensure each
* peripheral's driver can register a callback to cope with clock drift.
*/
static lsi_update_cb_t lsi_update_callbacks[NUM_SLOW_CLOCK_PERIPHERALS];
int stm32wb0_register_lsi_update_callback(lsi_update_cb_t cb)
{
for (size_t i = 0; i < ARRAY_SIZE(lsi_update_callbacks); i++) {
if (lsi_update_callbacks[i] == NULL) {
lsi_update_callbacks[i] = cb;
return 0;
}
}
return -ENOMEM;
}
static void radio_ctrl_isr(void)
{
/* Clear calibration complete flag / interrupt */
LL_RADIO_TIMER_ClearFlag_LSICalibrationEnded(RADIO_CTRL);
/* Release the measurement thread */
k_sem_give(&lsi_measurement_sema);
}
static void lsi_rt_measure_loop(void)
{
uint32_t old, new;
while (1) {
/* Sleep until calibration interval elapses */
k_sleep(K_MSEC(CONFIG_STM32WB0_LSI_RUNTIME_MEASUREMENT_INTERVAL));
old = stm32wb0_lsi_frequency;
/* Ensure the MR_BLE IP clock is enabled. */
if (!LL_APB2_GRP1_IsEnabledClock(LL_APB2_GRP1_PERIPH_MRBLE)) {
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_MRBLE);
}
/* Perform measurement, making sure we sleep on the semaphore
* signaled by the "measurement complete" interrupt handler
*/
measure_lsi_frequency(&lsi_measurement_sema);
new = stm32wb0_lsi_frequency;
/* If LSI frequency changed, invoke all registered callbacks */
if (new != old) {
for (size_t i = 0; i < ARRAY_SIZE(lsi_update_callbacks); i++) {
if (lsi_update_callbacks[i]) {
lsi_update_callbacks[i](stm32wb0_lsi_frequency);
}
}
}
}
}
#define LSI_RTM_THREAD_STACK_SIZE 512
#define LSI_RTM_THREAD_PRIORITY K_LOWEST_APPLICATION_THREAD_PRIO
K_KERNEL_THREAD_DEFINE(lsi_rt_measurement_thread,
LSI_RTM_THREAD_STACK_SIZE,
lsi_rt_measure_loop,
NULL, NULL, NULL,
LSI_RTM_THREAD_PRIORITY, /* No options */ 0,
/* No delay (automatic start by kernel) */ 0);
#endif
int stm32_clock_control_init(const struct device *dev)
{
ARG_UNUSED(dev);
/* Set flash latency according to target CLK_SYS frequency:
* - 1 wait state when CLK_SYS > 32MHz (i.e., 64MHz configuration)
* - 0 wait states otherwise (CLK_SYS <= 32MHz)
*/
LL_FLASH_SetLatency(
(CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC >= CLOCK_FREQ_32MHZ)
? LL_FLASH_LATENCY_1
: LL_FLASH_LATENCY_0
);
/* Unconditionally enable SYSCFG clock for other drivers */
LL_APB0_GRP1_EnableClock(LL_APB0_GRP1_PERIPH_SYSCFG);
/* Set up indiviual enabled clocks */
set_up_fixed_clock_sources();
/* Set up the slow clock mux */
#if defined(STM32_WB0_SLOWCLK_SRC)
LL_RCC_LSCO_SetSource(STM32_WB0_SLOWCLK_SRC);
#endif
#if defined(STM32_SYSCLK_SRC_HSE)
/* Select Direct HSE as SYSCLK source */
LL_RCC_DIRECT_HSE_Enable();
while (LL_RCC_DIRECT_HSE_IsEnabled() == 0) {
/* Wait until Direct HSE is ready */
}
#elif defined(STM32_SYSCLK_SRC_HSI) || defined(STM32_SYSCLK_SRC_PLL)
/* Select RC64MPLL (HSI/PLL) block as SYSCLK source. */
LL_RCC_DIRECT_HSE_Disable();
# if defined(STM32_SYSCLK_SRC_PLL)
BUILD_ASSERT(IS_ENABLED(STM32_HSE_ENABLED),
"STM32WB0 PLL requires HSE to be enabled!");
/* Turn on the PLL part of RC64MPLL block */
LL_RCC_RC64MPLL_Enable();
while (LL_RCC_RC64MPLL_IsReady() == 0) {
/* Wait until PLL is ready */
}
# endif /* STM32_SYSCLK_SRC_PLL */
#endif /* STM32_SYSCLK_SRC_* */
/* Set CLK_SYS prescaler */
LL_RCC_SetRC64MPLLPrescaler(
kconfig_to_ll_prescaler(STM32_WB0_CLKSYS_PRESCALER));
SystemCoreClock = CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC;
#if defined(STM32_LSI_ENABLED)
/* Enable MR_BLE clock for LSI measurement.
* This is needed because we use part of the MR_BLE hardware
* to perform this measurement.
*/
LL_APB2_GRP1_EnableClock(LL_APB2_GRP1_PERIPH_MRBLE);
/* Perform a measure of the LSI frequency */
measure_lsi_frequency(NULL);
#if CONFIG_STM32WB0_LSI_RUNTIME_MEASUREMENT_INTERVAL == 0
/* Disable the MR_BLE clock after the measurement */
LL_APB2_GRP1_DisableClock(LL_APB2_GRP1_PERIPH_MRBLE);
#else
/* MR_BLE clock must not be disabled, as we're
* about to access registers of the IP again.
*/
/* Enable LSI measurement complete IRQ at NVIC level */
IRQ_CONNECT(RADIO_CTRL_IRQn, IRQ_PRIO_LOWEST,
radio_ctrl_isr, NULL, 0);
irq_enable(RADIO_CTRL_IRQn);
/* Unmask IRQ at peripheral level */
LL_RADIO_TIMER_EnableLSICalibrationIT(RADIO_CTRL);
#endif /* CONFIG_STM32WB0_LSI_RUNTIME_MEASUREMENT_INTERVAL == 0 */
#endif /* STM32_LSI_ENABLED*/
return 0;
}
/**
* @brief Reset & Clock Controller device
* Note that priority is intentionally set to 1,
* so that RCC init runs just after SoC init.
*/
DEVICE_DT_DEFINE(STM32_CLOCK_CONTROL_NODE,
&stm32_clock_control_init,
NULL, NULL, NULL,
PRE_KERNEL_1,
CONFIG_CLOCK_CONTROL_INIT_PRIORITY,
&stm32_clock_control_api);