/* * Copyright (c) 2024 STMicroelectronics * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* 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 = / * * ==> 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);