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..93653ba6ea2f --- /dev/null +++ b/subsys/mpsl/clock_ctrl/mpsl_clock_ctrl.c @@ -0,0 +1,362 @@ +/* + * Copyright (c) 2025 Nordic Semiconductor ASA + * + * SPDX-License-Identifier: LicenseRef-Nordic-5-Clause + */ + +#include +#include +#include +#include + +#if defined(CONFIG_CLOCK_CONTROL_NRF) +#include +#endif /* CONFIG_CLOCK_CONTROL_NRF */ + +#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; + +/* 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); + +#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. + * + * 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 (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: + * # -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_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)) { + 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; + + /* 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); + + 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; + } + + /* 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_release(); + } + + atomic_dec(&m_lfclk_state.m_clk_refcnt); + + return 0; +} + +#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 */ + +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);