From 88a39b5bd75195d8e2ac4684408f029b3354e217 Mon Sep 17 00:00:00 2001 From: Piotr Pryga Date: Sat, 12 Oct 2024 22:55:35 +0200 Subject: [PATCH 1/4] mpsl: clock_ctrl: Integration layer with nRF clock control In the past MPSL used its own implementation of clock control. The approach changes due to lack of acces to clocks from Radio core in nRF54h20 SoC. Thaking this opportunity we provide experimental support for external clock control to nRF52 and nRF53 SoC Series. The new module is an integration layer between new MPSL API that allows for registration of external clock driver and nRF clock control driver. The implementation in this commit provides integration with MPSL for nrf clock control for nRF52 and nRF53 SoC series. Note: The support for nRF52 and nRF53 SoC series is experimental and is not enabled by default. Use of nrf clock control with MPSL allows to initialize the library in POST_KERNEL stage. Thanks to that we can use kernel synchronization primitives and non blocking waits. The change in MPSL init level and priority affects BT_LL_SOFTDEVIDE- _HCI_SYS_INIT_PRIORITY. The HCI driver for SDC depends on MPSL so it must be initialized after the MPSL. Signed-off-by: Piotr Pryga --- drivers/mpsl/clock_control/Kconfig | 4 +- subsys/bluetooth/controller/Kconfig | 8 + subsys/bluetooth/controller/hci_driver.c | 7 +- subsys/mpsl/CMakeLists.txt | 4 + subsys/mpsl/Kconfig | 1 + subsys/mpsl/clock_ctrl/CMakeLists.txt | 9 + subsys/mpsl/clock_ctrl/Kconfig | 15 ++ subsys/mpsl/clock_ctrl/mpsl_clock_ctrl.c | 234 +++++++++++++++++++++++ subsys/mpsl/clock_ctrl/mpsl_clock_ctrl.h | 33 ++++ subsys/mpsl/init/Kconfig | 12 +- subsys/mpsl/init/mpsl_init.c | 77 +++++--- 11 files changed, 378 insertions(+), 26 deletions(-) create mode 100644 subsys/mpsl/clock_ctrl/CMakeLists.txt create mode 100644 subsys/mpsl/clock_ctrl/Kconfig create mode 100644 subsys/mpsl/clock_ctrl/mpsl_clock_ctrl.c create mode 100644 subsys/mpsl/clock_ctrl/mpsl_clock_ctrl.h diff --git a/drivers/mpsl/clock_control/Kconfig b/drivers/mpsl/clock_control/Kconfig index 099c191e3ed3..7d86eace431a 100644 --- a/drivers/mpsl/clock_control/Kconfig +++ b/drivers/mpsl/clock_control/Kconfig @@ -8,6 +8,8 @@ config CLOCK_CONTROL_MPSL bool depends on MPSL depends on CLOCK_CONTROL - depends on !SOC_SERIES_NRF54HX + depends on !MPSL_USE_EXTERNAL_CLOCK_CONTROL default y select CLOCK_CONTROL_NRF_FORCE_ALT + help + Use the clock driver provided as part of the MPSL library. diff --git a/subsys/bluetooth/controller/Kconfig b/subsys/bluetooth/controller/Kconfig index a5397c7458a8..e87337b0954f 100644 --- a/subsys/bluetooth/controller/Kconfig +++ b/subsys/bluetooth/controller/Kconfig @@ -618,5 +618,13 @@ config BT_CTLR_SDC_LE_POWER_CLASS_1 See Bluetooth Core Specification, Vol 6, Part A, Section 3 Transmitter Characteristics for more information. +config BT_LL_SOFTDEVICE_INIT_PRIORITY + int + default 53 if MPSL_USE_EXTERNAL_CLOCK_CONTROL + default KERNEL_INIT_PRIORITY_DEFAULT + help + This option configures the LL_SOFTDEVICE initialization priority level. The priority + must be lower than the CONFIG_MPSL_INIT_PRIORITY due to dependency on the MPSL library. + endmenu endif # BT_LL_SOFTDEVICE diff --git a/subsys/bluetooth/controller/hci_driver.c b/subsys/bluetooth/controller/hci_driver.c index a4ab5259fba1..a29ef5dc347e 100644 --- a/subsys/bluetooth/controller/hci_driver.c +++ b/subsys/bluetooth/controller/hci_driver.c @@ -1523,10 +1523,15 @@ static int hci_driver_init(const struct device *dev) return err; } +#if defined(CONFIG_MPSL_USE_EXTERNAL_CLOCK_CONTROL) +BUILD_ASSERT(CONFIG_BT_LL_SOFTDEVICE_INIT_PRIORITY > CONFIG_MPSL_INIT_PRIORITY, + "MPSL must be initialized before SoftDevice Controller"); +#endif /* CONFIG_MPSL_USE_EXTERNAL_CLOCK_CONTROL */ + #define BT_HCI_CONTROLLER_INIT(inst) \ static struct hci_driver_data data_##inst; \ DEVICE_DT_INST_DEFINE(inst, hci_driver_init, NULL, &data_##inst, NULL, POST_KERNEL, \ - CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &hci_driver_api) + CONFIG_BT_LL_SOFTDEVICE_INIT_PRIORITY, &hci_driver_api) /* Only a single instance is supported */ BT_HCI_CONTROLLER_INIT(0) diff --git a/subsys/mpsl/CMakeLists.txt b/subsys/mpsl/CMakeLists.txt index 07167043c20d..d2feaeb892f9 100644 --- a/subsys/mpsl/CMakeLists.txt +++ b/subsys/mpsl/CMakeLists.txt @@ -18,4 +18,8 @@ if (CONFIG_MPSL_USE_ZEPHYR_PM) add_subdirectory(pm) endif() +if (CONFIG_MPSL_USE_EXTERNAL_CLOCK_CONTROL) + add_subdirectory(clock_ctrl) +endif() + add_subdirectory_ifdef(CONFIG_MPSL_PIN_DEBUG pin_debug) diff --git a/subsys/mpsl/Kconfig b/subsys/mpsl/Kconfig index 578622d8f37c..142df76a7809 100644 --- a/subsys/mpsl/Kconfig +++ b/subsys/mpsl/Kconfig @@ -20,6 +20,7 @@ rsource "cx/Kconfig" rsource "init/Kconfig" rsource "pin_debug/Kconfig" rsource "pm/Kconfig" +rsource "clock_ctrl/Kconfig" endif # !MPSL_FEM_ONLY diff --git a/subsys/mpsl/clock_ctrl/CMakeLists.txt b/subsys/mpsl/clock_ctrl/CMakeLists.txt new file mode 100644 index 000000000000..5000979411d5 --- /dev/null +++ b/subsys/mpsl/clock_ctrl/CMakeLists.txt @@ -0,0 +1,9 @@ +# +# Copyright (c) 2025 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +zephyr_library() + +zephyr_library_sources(mpsl_clock_ctrl.c) diff --git a/subsys/mpsl/clock_ctrl/Kconfig b/subsys/mpsl/clock_ctrl/Kconfig new file mode 100644 index 000000000000..7f82cc43f55b --- /dev/null +++ b/subsys/mpsl/clock_ctrl/Kconfig @@ -0,0 +1,15 @@ +# +# Copyright (c) 2025 Nordic Semiconductor ASA +# +# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause +# + +config MPSL_USE_EXTERNAL_CLOCK_CONTROL + bool "Use external clock control [EXPERIMENTAL]" + depends on MPSL + select CLOCK_CONTROL + default y if SOC_SERIES_NRF54HX + select EXPERIMENTAL if SOC_SERIES_NRF52X || SOC_SERIES_NRF53X + help + This option configures MPSL to use an external clock driver, and + not the clock driver provided as part of the MPSL library. diff --git a/subsys/mpsl/clock_ctrl/mpsl_clock_ctrl.c b/subsys/mpsl/clock_ctrl/mpsl_clock_ctrl.c new file mode 100644 index 000000000000..d95d35e546b7 --- /dev/null +++ b/subsys/mpsl/clock_ctrl/mpsl_clock_ctrl.c @@ -0,0 +1,234 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include +#include +#include + +#if defined(CONFIG_CLOCK_CONTROL_NRF) +#include +#endif /* CONFIG_CLOCK_CONTROL_NRF */ + +#include +#include "mpsl_clock_ctrl.h" + +/* Variable shared for nrf and nrf2 clock control */ +static atomic_t m_hfclk_refcnt; + +/* Type use to get information about a clock request status */ +struct clock_onoff_state { + struct onoff_client cli; + atomic_t m_clk_ready; + atomic_t m_clk_refcnt; + struct k_sem sem; + int clk_req_rsp; +}; + +static struct clock_onoff_state m_lfclk_state; + +static int32_t m_lfclk_release(void); + +#define MPSL_LFCLK_REQUEST_WAIT_TIMEOUT_MS 1000 + +/** @brief LFCLK request callback. + * + * The callback function provided to clock control to notify about LFCLK request being finished. + */ +static void lfclk_request_cb(struct onoff_manager *mgr, struct onoff_client *cli, uint32_t state, + int res) +{ + struct clock_onoff_state *clock_state = CONTAINER_OF(cli, struct clock_onoff_state, cli); + + clock_state->clk_req_rsp = res; + k_sem_give(&clock_state->sem); +} + +/** @brief Wait for LFCLK to be ready. + * + * The function can time out if there is no response from clock control drvier until + * MPSL_LFCLK_REQUEST_WAIT_TIMEOUT_MS. + * + * @note For nRF54H SoC series waiting for LFCLK can't block the system work queue. The nrf2 clock + * control driver can return -TIMEDOUT due not handled response from sysctrl. + * + * @retval 0 LFCLK is ready. + * @retval -NRF_EINVAL There were no LFCLK request. + * @retval -NRF_EFAULT LFCLK request failed. + */ +static int32_t m_lfclk_wait(void) +{ + int32_t err; + + if (atomic_get(&m_lfclk_state.m_clk_ready) == (atomic_val_t) true) { + return 0; + } + + /* Check if lfclk has been requested */ + if (atomic_get(&m_lfclk_state.m_clk_refcnt) <= (atomic_val_t)0) { + return -NRF_EINVAL; + } + + /* Wait for response from clock control */ + err = k_sem_take(&m_lfclk_state.sem, K_MSEC(MPSL_LFCLK_REQUEST_WAIT_TIMEOUT_MS)); + if (err < 0) { + /* Do a gracefull cancel of the request, the function release does this + * as well as and relase. + */ + (void)m_lfclk_release(); + + return -NRF_EFAULT; + } + + if (m_lfclk_state.clk_req_rsp < 0) { + __ASSERT(false, "LFCLK could not be started, reason: %d", + m_lfclk_state.clk_req_rsp); + /* Possible failure reasons: + * # -ERRTIMEDOUT - nRFS service timeout + * # -EIO - nRFS service error + * # -ENXIO - request rejected + * All these mean failure for MPSL. + */ + return -NRF_EFAULT; + } + + atomic_set(&m_lfclk_state.m_clk_ready, (atomic_val_t) true); + + return 0; +} + +#if defined(CONFIG_CLOCK_CONTROL_NRF) + +static void m_lfclk_calibration_start(void) +{ + if (IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_DRIVER_CALIBRATION)) { + z_nrf_clock_calibration_force_start(); + } +} + +static bool m_lfclk_calibration_is_enabled(void) +{ + if (IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_DRIVER_CALIBRATION)) { + return true; + } else { + return false; + } +} + +static int32_t m_lfclk_request(void) +{ + struct onoff_manager *mgr = z_nrf_clock_control_get_onoff(CLOCK_CONTROL_NRF_SUBSYS_LF); + int32_t err; + + sys_notify_init_callback(&m_lfclk_state.cli.notify, lfclk_request_cb); + (void)k_sem_init(&m_lfclk_state.sem, 0, 1); + + err = onoff_request(mgr, &m_lfclk_state.cli); + if (err < 0) { + return err; + } + + atomic_inc(&m_lfclk_state.m_clk_refcnt); + + return 0; +} + +static int32_t m_lfclk_release(void) +{ + struct onoff_manager *mgr = z_nrf_clock_control_get_onoff(CLOCK_CONTROL_NRF_SUBSYS_LF); + int32_t err; + + /* In case there is other ongoing request, cancel it. */ + err = onoff_cancel_or_release(mgr, &m_lfclk_state.cli); + if (err < 0) { + return err; + } + + atomic_dec(&m_lfclk_state.m_clk_refcnt); + + return 0; +} + +static void m_hfclk_request(void) +{ + /* The z_nrf_clock_bt_ctlr_hf_request doesn't count references to HFCLK, + * it is caller responsibility handle requests and releases counting. + */ + if (atomic_inc(&m_hfclk_refcnt) > (atomic_val_t)0) { + return; + } + + z_nrf_clock_bt_ctlr_hf_request(); +} + +static void m_hfclk_release(void) +{ + /* The z_nrf_clock_bt_ctlr_hf_request doesn't count references to HFCLK, + * it is caller responsibility to not release the clock if there is + * other request pending. + */ + if (atomic_get(&m_hfclk_refcnt) < (atomic_val_t)1) { + LOG_WRN("Mismatch between HFCLK request/release"); + return; + } + + if (atomic_dec(&m_hfclk_refcnt) > (atomic_val_t)1) { + return; + } + + z_nrf_clock_bt_ctlr_hf_release(); +} + +static bool m_hfclk_is_running(void) +{ + if (atomic_get(&m_hfclk_refcnt) > (atomic_val_t)0) { + nrf_clock_hfclk_t type; + + unsigned int key = irq_lock(); + + (void)nrfx_clock_is_running(NRF_CLOCK_DOMAIN_HFCLK, &type); + + irq_unlock(key); + + return ((type == NRF_CLOCK_HFCLK_HIGH_ACCURACY) ? true : false); + } + + return false; +} + +#else +#error "Unsupported clock control" +#endif /* CONFIG_CLOCK_CONTROL_NRF */ + +static mpsl_clock_lfclk_ctrl_source_t m_nrf_lfclk_ctrl_data = { + .lfclk_wait = m_lfclk_wait, + .lfclk_calibration_start = m_lfclk_calibration_start, + .lfclk_calibration_is_enabled = m_lfclk_calibration_is_enabled, + .lfclk_request = m_lfclk_request, + .lfclk_release = m_lfclk_release, +#if defined(CONFIG_CLOCK_CONTROL_NRF_ACCURACY) + .accuracy_ppm = CONFIG_CLOCK_CONTROL_NRF_ACCURACY, +#else + .accuracy_ppm = MPSL_LFCLK_ACCURACY_PPM, +#endif /* CONFIG_CLOCK_CONTROL_NRF_ACCURACY */ + .skip_wait_lfclk_started = IS_ENABLED(CONFIG_SYSTEM_CLOCK_NO_WAIT) +}; + +static mpsl_clock_hfclk_ctrl_source_t m_nrf_hfclk_ctrl_data = { + .hfclk_request = m_hfclk_request, + .hfclk_release = m_hfclk_release, + .hfclk_is_running = m_hfclk_is_running, + .startup_time_us = CONFIG_MPSL_HFCLK_LATENCY +}; + +int32_t mpsl_clock_ctrl_init(void) +{ + return mpsl_clock_ctrl_source_register(&m_nrf_lfclk_ctrl_data, &m_nrf_hfclk_ctrl_data); +} + +int32_t mpsl_clock_ctrl_uninit(void) +{ + return mpsl_clock_ctrl_source_unregister(); +} diff --git a/subsys/mpsl/clock_ctrl/mpsl_clock_ctrl.h b/subsys/mpsl/clock_ctrl/mpsl_clock_ctrl.h new file mode 100644 index 000000000000..f3f53c6a154c --- /dev/null +++ b/subsys/mpsl/clock_ctrl/mpsl_clock_ctrl.h @@ -0,0 +1,33 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#ifndef MPSL_CLOCK_CTRL_H__ +#define MPSL_CLOCK_CTRL_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +/** @brief Initialize MPSL integration with NRF clock control + * + * @retval 0 MPSL clock integration initialized successfully. + * @retval -NRF_EPERM Clock control is already initialized. + * @retval -NRF_EINVAL Invalid parameters supplied. + */ +int32_t mpsl_clock_ctrl_init(void); + +/** @brief Uninitialize MPSL integration with NRF clock control + * + * @retval 0 MPSL clock was uninitialized successfully. + * @retval -NRF_EPERM MPSL was not initialized before the call. + */ +int32_t mpsl_clock_ctrl_uninit(void); + +#ifdef __cplusplus +} +#endif + +#endif /* MPSL_CLOCK_CTRL_H__ */ diff --git a/subsys/mpsl/init/Kconfig b/subsys/mpsl/init/Kconfig index 36ccf6244c22..c7bf4b00a8f1 100644 --- a/subsys/mpsl/init/Kconfig +++ b/subsys/mpsl/init/Kconfig @@ -50,6 +50,7 @@ config MPSL_DYNAMIC_INTERRUPTS config MPSL_TRIGGER_IPC_TASK_ON_RTC_START bool "Trigger an IPC task when the RTC starts" depends on SOC_NRF5340_CPUNET + depends on CLOCK_CONTROL_MPSL help This option configures MPSL to trigger an IPC task at the time the RTC starts. This can be used for synchronizing time betwen the @@ -98,13 +99,22 @@ config MPSL_HFCLK_LATENCY config MPSL_CALIBRATION_PERIOD int "Calibration callback period in milliseconds" - depends on (SOC_SERIES_NRF54LX || CLOCK_CONTROL_NRF_K32SRC_RC_CALIBRATION) && !SOC_SERIES_NRF54HX + depends on CLOCK_CONTROL_MPSL && (SOC_SERIES_NRF54LX || CLOCK_CONTROL_NRF_K32SRC_RC_CALIBRATION) default CLOCK_CONTROL_NRF_CALIBRATION_PERIOD if CLOCK_CONTROL_NRF_K32SRC_RC_CALIBRATION default 4000 help This configuration means how often the calibration callback to mpsl is called. On 54L, this still needs to be called even if LFRC is not used. +config MPSL_INIT_PRIORITY + int + default 52 if MPSL_USE_EXTERNAL_CLOCK_CONTROL + default KERNEL_INIT_PRIORITY_DEFAULT + help + This option configures MPSL system init priority level. For nRF54H SoC series the priority + must be lower than CONFIG_NRFS_BACKEND_IPC_SERVICE_INIT_PRIO. The nrf2 clock control depends + on the nRFS backend. + module=MPSL module-str=MPSL source "${ZEPHYR_BASE}/subsys/logging/Kconfig.template.log_config" diff --git a/subsys/mpsl/init/mpsl_init.c b/subsys/mpsl/init/mpsl_init.c index f0a6c76ac405..af1131ad601b 100644 --- a/subsys/mpsl/init/mpsl_init.c +++ b/subsys/mpsl/init/mpsl_init.c @@ -29,12 +29,16 @@ #include #endif +#if IS_ENABLED(CONFIG_MPSL_USE_EXTERNAL_CLOCK_CONTROL) +#include "../clock_ctrl/mpsl_clock_ctrl.h" +#endif /* CONFIG_MPSL_USE_EXTERNAL_CLOCK_CONTROL */ + LOG_MODULE_REGISTER(mpsl_init, CONFIG_MPSL_LOG_LEVEL); #if defined(CONFIG_MPSL_CALIBRATION_PERIOD) static void mpsl_calibration_work_handler(struct k_work *work); static K_WORK_DELAYABLE_DEFINE(calibration_work, mpsl_calibration_work_handler); -#endif /* CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC && !CONFIG_SOC_SERIES_NRF54HX */ +#endif /* CONFIG_MPSL_CALIBRATION_PERIOD */ extern void rtc_pretick_rtc0_isr_hook(void); @@ -310,7 +314,7 @@ static void m_assert_handler(const char *const file, const uint32_t line) } #endif /* IS_ENABLED(CONFIG_MPSL_ASSERT_HANDLER) */ -#if !defined(CONFIG_SOC_SERIES_NRF54HX) +#if !defined(CONFIG_MPSL_USE_EXTERNAL_CLOCK_CONTROL) static uint8_t m_config_clock_source_get(void) { #ifdef CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC @@ -328,7 +332,7 @@ static uint8_t m_config_clock_source_get(void) return 0; #endif } -#endif /* !CONFIG_SOC_SERIES_NRF54HX */ +#endif /* !CONFIG_MPSL_USE_EXTERNAL_CLOCK_CONTROL */ #if defined(CONFIG_MPSL_CALIBRATION_PERIOD) static atomic_t do_calibration; @@ -346,23 +350,25 @@ static void mpsl_calibration_work_handler(struct k_work *work) mpsl_work_schedule(&calibration_work, K_MSEC(CONFIG_MPSL_CALIBRATION_PERIOD)); } -#endif /* CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC && !CONFIG_SOC_SERIES_NRF54HX */ +#endif /* CONFIG_MPSL_CALIBRATION_PERIOD */ static int32_t mpsl_lib_init_internal(void) { int err = 0; +#if !defined(CONFIG_MPSL_USE_EXTERNAL_CLOCK_CONTROL) mpsl_clock_lfclk_cfg_t clock_cfg; +#endif /* CONFIG_MPSL_USE_EXTERNAL_CLOCK_CONTROL */ -#ifdef CONFIG_MPSL_TRIGGER_IPC_TASK_ON_RTC_START +#if defined(CONFIG_MPSL_TRIGGER_IPC_TASK_ON_RTC_START) nrf_ipc_send_config_set(NRF_IPC, CONFIG_MPSL_TRIGGER_IPC_TASK_ON_RTC_START_CHANNEL, (1UL << CONFIG_MPSL_TRIGGER_IPC_TASK_ON_RTC_START_CHANNEL)); mpsl_clock_task_trigger_on_rtc_start_set( (uint32_t)&NRF_IPC->TASKS_SEND[CONFIG_MPSL_TRIGGER_IPC_TASK_ON_RTC_START_CHANNEL]); -#endif +#endif /* CONFIG_MPSL_TRIGGER_IPC_TASK_ON_RTC_START */ /* TODO: Clock config should be adapted in the future to new architecture. */ -#if !defined(CONFIG_SOC_SERIES_NRF54HX) +#if !defined(CONFIG_MPSL_USE_EXTERNAL_CLOCK_CONTROL) clock_cfg.source = m_config_clock_source_get(); clock_cfg.accuracy_ppm = CONFIG_CLOCK_CONTROL_NRF_ACCURACY; clock_cfg.skip_wait_lfclk_started = @@ -382,15 +388,15 @@ static int32_t mpsl_lib_init_internal(void) #else clock_cfg.rc_ctiv = 0; clock_cfg.rc_temp_ctiv = 0; -#endif -#else - /* For now just set the values to 0 to avoid "use of uninitialized variable" warnings. - * MPSL assumes the clocks are always available and does currently not implement - * clock handling on these platforms. The LFCLK is expected to have an accuracy of - * 500ppm or better regardless of the value passed in clock_cfg. - */ - memset(&clock_cfg, 0, sizeof(clock_cfg)); -#endif +#endif /* CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC */ +#endif /* !CONFIG_MPSL_USE_EXTERNAL_CLOCK_CONTROL */ + +#if defined(CONFIG_MPSL_USE_EXTERNAL_CLOCK_CONTROL) + err = mpsl_clock_ctrl_init(); + if (err) { + return err; + } +#endif /* CONFIG_MPSL_USE_EXTERNAL_CLOCK_CONTROL */ #if defined(CONFIG_SOC_SERIES_NRF54HX) /* Secure domain no longer enables DPPI channels for local domains, @@ -399,13 +405,18 @@ static int32_t mpsl_lib_init_internal(void) nrf_dppi_channels_enable(NRF_DPPIC130, DPPI_SINK_CHANNELS); nrf_dppi_channels_enable(NRF_DPPIC132, DPPI_SOURCE_CHANNELS); #endif +#if defined(CONFIG_MPSL_USE_EXTERNAL_CLOCK_CONTROL) + err = mpsl_init(NULL, CONFIG_MPSL_LOW_PRIO_IRQN, m_assert_handler); +#else err = mpsl_init(&clock_cfg, CONFIG_MPSL_LOW_PRIO_IRQN, m_assert_handler); +#endif /* CONFIG_MPSL_USE_EXTERNAL_CLOCK_CONTROL */ if (err) { return err; } +#if !defined(CONFIG_MPSL_USE_EXTERNAL_CLOCK_CONTROL) mpsl_clock_hfclk_latency_set(CONFIG_MPSL_HFCLK_LATENCY); - +#endif /* !CONFIG_MPSL_USE_EXTERNAL_CLOCK_CONTROL */ if (IS_ENABLED(CONFIG_SOC_NRF_FORCE_CONSTLAT) && !IS_ENABLED(CONFIG_SOC_COMPATIBLE_NRF54LX)) { mpsl_pan_rfu(); @@ -417,7 +428,7 @@ static int32_t mpsl_lib_init_internal(void) if (err) { return err; } -#endif +#endif /* MPSL_TIMESLOT_SESSION_COUNT > 0 */ return 0; } @@ -481,7 +492,7 @@ static int mpsl_low_prio_init(void) atomic_set(&do_calibration, 1); mpsl_work_schedule(&calibration_work, K_MSEC(CONFIG_MPSL_CALIBRATION_PERIOD)); -#endif /* CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC && !CONFIG_SOC_SERIES_NRF54HX */ +#endif /* CONFIG_MPSL_CALIBRATION_PERIOD */ return 0; } @@ -509,12 +520,21 @@ int32_t mpsl_lib_uninit(void) #if IS_ENABLED(CONFIG_MPSL_DYNAMIC_INTERRUPTS) #if defined(CONFIG_MPSL_CALIBRATION_PERIOD) atomic_set(&do_calibration, 0); -#endif /* CONFIG_CLOCK_CONTROL_NRF_K32SRC_RC && !CONFIG_SOC_SERIES_NRF54HX */ +#endif /* CONFIG_MPSL_CALIBRATION_PERIOD */ mpsl_lib_irq_disable(); mpsl_uninit(); +#if defined(CONFIG_MPSL_USE_EXTERNAL_CLOCK_CONTROL) + int err; + + err = mpsl_clock_ctrl_uninit(); + if (err) { + return err; + } +#endif /* CONFIG_MPSL_USE_EXTERNAL_CLOCK_CONTROL */ + return 0; #else /* !IS_ENABLED(CONFIG_MPSL_DYNAMIC_INTERRUPTS) */ return -NRF_EPERM; @@ -541,6 +561,17 @@ void mpsl_lowpower_request_callback(void) } #endif /* defined(CONFIG_SOC_COMPATIBLE_NRF54LX) */ -SYS_INIT(mpsl_lib_init_sys, PRE_KERNEL_1, CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); -SYS_INIT(mpsl_low_prio_init, POST_KERNEL, - CONFIG_KERNEL_INIT_PRIORITY_DEFAULT); +#if defined(CONFIG_MPSL_USE_EXTERNAL_CLOCK_CONTROL) +#define MPSL_INIT_LEVEL POST_KERNEL +#else +#define MPSL_INIT_LEVEL PRE_KERNEL_1 +#endif /* CONFIG_MPSL_USE_EXTERNAL_CLOCK_CONTROL */ + +#if defined(CONFIG_MPSL_USE_EXTERNAL_CLOCK_CONTROL) && \ + defined(CONFIG_NRFS_BACKEND_IPC_SERVICE_INIT_PRIO) +BUILD_ASSERT(CONFIG_MPSL_INIT_PRIORITY > CONFIG_NRFS_BACKEND_IPC_SERVICE_INIT_PRIO, + "MPSL must be initialized after NRFS IPC"); +#endif /* CONFIG_MPSL_USE_EXTERNAL_CLOCK_CONTROL && CONFIG_NRFS_BACKEND_IPC_SERVICE_INIT_PRIO */ + +SYS_INIT(mpsl_lib_init_sys, MPSL_INIT_LEVEL, CONFIG_MPSL_INIT_PRIORITY); +SYS_INIT(mpsl_low_prio_init, POST_KERNEL, CONFIG_MPSL_INIT_PRIORITY); From e6d42a11ef5c087626292af7102d458f18e3c77c Mon Sep 17 00:00:00 2001 From: Piotr Pryga Date: Thu, 28 Nov 2024 22:28:19 +0100 Subject: [PATCH 2/4] mpsl: clock_ctrl: Add integration layer for nRF54H20 Add integration layer for MPSL external clock driver and nrf2 clock control for nRF5420. This is mandatory for the nRF54H20. Note: The nrf2 clock control requires the MPSL initialization to be done later. The nrf2 clock control depends on nRFS that is initialized at POST_KENREL init level. Its init priority is CONFIG_NRFS_BACKEND_IPC_SERVICE_INIT_PRIO that is lower than former MPSL init level. To make sure the mpsl lfclk request and response is handled corrently we must make the MPSL is initialized after it. Signed-off-by: Piotr Pryga --- subsys/mpsl/clock_ctrl/mpsl_clock_ctrl.c | 104 +++++++++++++++++++++++ 1 file changed, 104 insertions(+) diff --git a/subsys/mpsl/clock_ctrl/mpsl_clock_ctrl.c b/subsys/mpsl/clock_ctrl/mpsl_clock_ctrl.c index d95d35e546b7..7d1158883a73 100644 --- a/subsys/mpsl/clock_ctrl/mpsl_clock_ctrl.c +++ b/subsys/mpsl/clock_ctrl/mpsl_clock_ctrl.c @@ -31,7 +31,11 @@ static struct clock_onoff_state m_lfclk_state; static int32_t m_lfclk_release(void); +#if defined(CONFIG_CLOCK_CONTROL_NRF2_NRFS_CLOCK_TIMEOUT_MS) +#define MPSL_LFCLK_REQUEST_WAIT_TIMEOUT_MS CONFIG_CLOCK_CONTROL_NRF2_NRFS_CLOCK_TIMEOUT_MS +#else #define MPSL_LFCLK_REQUEST_WAIT_TIMEOUT_MS 1000 +#endif /* CONFIG_CLOCK_CONTROL_NRF2_NRFS_CLOCK_TIMEOUT_MS */ /** @brief LFCLK request callback. * @@ -198,6 +202,106 @@ static bool m_hfclk_is_running(void) return false; } +#elif defined(CONFIG_CLOCK_CONTROL_NRF2) + +/* Temporary macro because there is no system level configuration of LFCLK source and its accuracy + * for nRF54H SoC series. What more, there is no API to retrieve the information about accuracy of + * available LFCLK. + */ +#define MPSL_LFCLK_ACCURACY_PPM 500 + +static const struct nrf_clock_spec m_lfclk_specs = { + .frequency = 32768, + .accuracy = MPSL_LFCLK_ACCURACY_PPM, + /* This affects selected LFCLK source. It doesn't switch to higher accuracy but selects more + * precise but current hungry lfclk source. + */ + .precision = NRF_CLOCK_CONTROL_PRECISION_DEFAULT, +}; + +static void m_lfclk_calibration_start(void) +{ + /* This function is not supported when CONFIG_CLOCK_CONTROL_NRF2 is set. + * As of now MPSL does not use this API in this configuration. + */ + __ASSERT_NO_MSG(false); +} + +static bool m_lfclk_calibration_is_enabled(void) +{ + /* This function should not be called from MPSL if CONFIG_CLOCK_CONTROL_NRF2 is set */ + __ASSERT_NO_MSG(false); + return false; +} + +static int32_t m_lfclk_request(void) +{ + const struct device *lfclk_dev = DEVICE_DT_GET(DT_NODELABEL(lfclk)); + int err; + + sys_notify_init_callback(&m_lfclk_state.cli.notify, lfclk_request_cb); + k_sem_init(&m_lfclk_state.sem, 0, 1); + + err = nrf_clock_control_request(lfclk_dev, &m_lfclk_specs, &m_lfclk_state.cli); + if (err < 0) { + return err; + } + + atomic_inc(&m_lfclk_state.m_clk_refcnt); + + return 0; +} + +static int32_t m_lfclk_release(void) +{ + const struct device *lfclk_dev = DEVICE_DT_GET(DT_NODELABEL(lfclk)); + int err; + + err = nrf_clock_control_cancel_or_release(lfclk_dev, &m_lfclk_specs, &m_lfclk_state.cli); + if (err < 0) { + return err; + } + + atomic_dec(&m_lfclk_state.m_clk_refcnt); + + return 0; +} + +static void m_hfclk_request(void) +{ + if (atomic_inc(&m_hfclk_refcnt) > (atomic_val_t)0) { + return; + } + + nrf_clock_control_hfxo_request(); +} + +static void m_hfclk_release(void) +{ + if (atomic_get(&m_hfclk_refcnt) < (atomic_val_t)1) { + LOG_WRN("Mismatch between HFCLK request/release"); + return; + } + + if (atomic_dec(&m_hfclk_refcnt) > (atomic_val_t)1) { + return; + } + + nrf_clock_control_hfxo_release(); +} + +static bool m_hfclk_is_running(void) +{ + /* As of now assume the HFCLK is running after the request was put. + * This puts the responsibility to the user to check if the time + * since last request is larger than the HFXO rampup time. + */ + if (atomic_get(&m_hfclk_refcnt) < (atomic_val_t)1) { + return false; + } + + return true; +} #else #error "Unsupported clock control" #endif /* CONFIG_CLOCK_CONTROL_NRF */ From 5ef171cd54a890cda82d4cbc55ccd68ae242836b Mon Sep 17 00:00:00 2001 From: Piotr Pryga Date: Fri, 6 Dec 2024 14:20:28 +0100 Subject: [PATCH 3/4] mpsl: clock_ctrl: Fix nRF52 LFCLK accuracy when LFSYNTH selected The nrf clock control driver doesn't enable HFXO when LFSYNTH is selected as a source of LFCLK. That causes the accuracy of LFCLK to be not within the expected by BT Core Specification range up to 500 ppm. To fix the problem the mpsl clock control integration layer has to request the hfxo in case the lfclk is requested with LFSYNTH as a source. Use of z_nrf_clock_bt_ctlr_hf_release makes the call to be faster. That unfortunately requires reference counting do avoid release of HFXO later in runtime by MPSL. For that reason the m_hfclk_request and m_hfclk_release are used. Signed-off-by: Piotr Pryga --- subsys/mpsl/clock_ctrl/mpsl_clock_ctrl.c | 106 +++++++++++++---------- 1 file changed, 60 insertions(+), 46 deletions(-) diff --git a/subsys/mpsl/clock_ctrl/mpsl_clock_ctrl.c b/subsys/mpsl/clock_ctrl/mpsl_clock_ctrl.c index 7d1158883a73..c43eea2ca564 100644 --- a/subsys/mpsl/clock_ctrl/mpsl_clock_ctrl.c +++ b/subsys/mpsl/clock_ctrl/mpsl_clock_ctrl.c @@ -105,6 +105,53 @@ static int32_t m_lfclk_wait(void) #if defined(CONFIG_CLOCK_CONTROL_NRF) +static void m_hfclk_request(void) +{ + /* The z_nrf_clock_bt_ctlr_hf_request doesn't count references to HFCLK, + * it is caller responsibility handle requests and releases counting. + */ + if (atomic_inc(&m_hfclk_refcnt) > (atomic_val_t)0) { + return; + } + + z_nrf_clock_bt_ctlr_hf_request(); +} + +static void m_hfclk_release(void) +{ + /* The z_nrf_clock_bt_ctlr_hf_request doesn't count references to HFCLK, + * it is caller responsibility to not release the clock if there is + * other request pending. + */ + if (atomic_get(&m_hfclk_refcnt) < (atomic_val_t)1) { + LOG_WRN("Mismatch between HFCLK request/release"); + return; + } + + if (atomic_dec(&m_hfclk_refcnt) > (atomic_val_t)1) { + return; + } + + z_nrf_clock_bt_ctlr_hf_release(); +} + +static bool m_hfclk_is_running(void) +{ + if (atomic_get(&m_hfclk_refcnt) > (atomic_val_t)0) { + nrf_clock_hfclk_t type; + + unsigned int key = irq_lock(); + + (void)nrfx_clock_is_running(NRF_CLOCK_DOMAIN_HFCLK, &type); + + irq_unlock(key); + + return ((type == NRF_CLOCK_HFCLK_HIGH_ACCURACY) ? true : false); + } + + return false; +} + static void m_lfclk_calibration_start(void) { if (IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_DRIVER_CALIBRATION)) { @@ -126,6 +173,13 @@ static int32_t m_lfclk_request(void) struct onoff_manager *mgr = z_nrf_clock_control_get_onoff(CLOCK_CONTROL_NRF_SUBSYS_LF); int32_t err; + /* Workaround for NRFX-6865. The nrf clock control as well as nrfx_clock doesn't enable + * HFXO when LFSYNTH is selected as LFCLK source. Remove the code when nrfx is fixed. + */ + if (IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_K32SRC_SYNTH)) { + m_hfclk_request(); + } + sys_notify_init_callback(&m_lfclk_state.cli.notify, lfclk_request_cb); (void)k_sem_init(&m_lfclk_state.sem, 0, 1); @@ -150,56 +204,16 @@ static int32_t m_lfclk_release(void) return err; } - atomic_dec(&m_lfclk_state.m_clk_refcnt); - - return 0; -} - -static void m_hfclk_request(void) -{ - /* The z_nrf_clock_bt_ctlr_hf_request doesn't count references to HFCLK, - * it is caller responsibility handle requests and releases counting. + /* Workaround for NRFX-6865. The nrf clock control as well as nrfx_clock doesn't enable + * HFXO when LFSYNTH is selected as LFCLK source. Remove the code when nrfx is fixed. */ - if (atomic_inc(&m_hfclk_refcnt) > (atomic_val_t)0) { - return; + if (IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_K32SRC_SYNTH)) { + m_hfclk_release(); } - z_nrf_clock_bt_ctlr_hf_request(); -} - -static void m_hfclk_release(void) -{ - /* The z_nrf_clock_bt_ctlr_hf_request doesn't count references to HFCLK, - * it is caller responsibility to not release the clock if there is - * other request pending. - */ - if (atomic_get(&m_hfclk_refcnt) < (atomic_val_t)1) { - LOG_WRN("Mismatch between HFCLK request/release"); - return; - } - - if (atomic_dec(&m_hfclk_refcnt) > (atomic_val_t)1) { - return; - } - - z_nrf_clock_bt_ctlr_hf_release(); -} - -static bool m_hfclk_is_running(void) -{ - if (atomic_get(&m_hfclk_refcnt) > (atomic_val_t)0) { - nrf_clock_hfclk_t type; - - unsigned int key = irq_lock(); - - (void)nrfx_clock_is_running(NRF_CLOCK_DOMAIN_HFCLK, &type); - - irq_unlock(key); - - return ((type == NRF_CLOCK_HFCLK_HIGH_ACCURACY) ? true : false); - } + atomic_dec(&m_lfclk_state.m_clk_refcnt); - return false; + return 0; } #elif defined(CONFIG_CLOCK_CONTROL_NRF2) From 233c61a94e9a9e67bcdd45436bd8b27253737884 Mon Sep 17 00:00:00 2001 From: Piotr Pryga Date: Mon, 3 Feb 2025 08:36:40 +0100 Subject: [PATCH 4/4] mpsl: clock_ctrl: Allow LFCLK timeout on nRF54H20 due to sysctrl IPC issue Add temporary workaround that allows the LFCLK to timeout on nRF54H20 (if CLOCK_CONTROL_NRF2 is enabled). The potential timeout is not an issue for now because the integration layer request the lowest accuracy of LFCLK and such or better LFCLK should be runing from boot of the radio core. Signed-off-by: Piotr Pryga --- subsys/mpsl/clock_ctrl/mpsl_clock_ctrl.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/subsys/mpsl/clock_ctrl/mpsl_clock_ctrl.c b/subsys/mpsl/clock_ctrl/mpsl_clock_ctrl.c index c43eea2ca564..93653ba6ea2f 100644 --- a/subsys/mpsl/clock_ctrl/mpsl_clock_ctrl.c +++ b/subsys/mpsl/clock_ctrl/mpsl_clock_ctrl.c @@ -7,6 +7,7 @@ #include #include #include +#include #if defined(CONFIG_CLOCK_CONTROL_NRF) #include @@ -15,6 +16,8 @@ #include #include "mpsl_clock_ctrl.h" +LOG_MODULE_REGISTER(mpsl_clock_ctrl, CONFIG_MPSL_LOG_LEVEL); + /* Variable shared for nrf and nrf2 clock control */ static atomic_t m_hfclk_refcnt; @@ -86,7 +89,14 @@ static int32_t m_lfclk_wait(void) return -NRF_EFAULT; } - if (m_lfclk_state.clk_req_rsp < 0) { + if (IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF2) && m_lfclk_state.clk_req_rsp == -ETIMEDOUT) { + /* Due to NCSDK-31169, temporarily allow for LFCLK request to timeout. + * That doens't break anything now because the LFCLK requested clock is + * 500PPM and such LFCLK should be running from boot of the radio core. + */ + LOG_WRN("LFCLK could not be started: %d", m_lfclk_state.clk_req_rsp); + return 0; + } else if (m_lfclk_state.clk_req_rsp < 0) { __ASSERT(false, "LFCLK could not be started, reason: %d", m_lfclk_state.clk_req_rsp); /* Possible failure reasons: