Skip to content

Commit f3d45ca

Browse files
ppryga-nordicnordicjm
authored andcommitted
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 <[email protected]>
1 parent a9bfb53 commit f3d45ca

File tree

11 files changed

+378
-26
lines changed

11 files changed

+378
-26
lines changed

drivers/mpsl/clock_control/Kconfig

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ config CLOCK_CONTROL_MPSL
88
bool
99
depends on MPSL
1010
depends on CLOCK_CONTROL
11-
depends on !SOC_SERIES_NRF54HX
11+
depends on !MPSL_USE_EXTERNAL_CLOCK_CONTROL
1212
default y
1313
select CLOCK_CONTROL_NRF_FORCE_ALT
14+
help
15+
Use the clock driver provided as part of the MPSL library.

subsys/bluetooth/controller/Kconfig

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -615,5 +615,13 @@ config BT_CTLR_SDC_LE_POWER_CLASS_1
615615
See Bluetooth Core Specification, Vol 6, Part A, Section 3
616616
Transmitter Characteristics for more information.
617617

618+
config BT_LL_SOFTDEVICE_INIT_PRIORITY
619+
int
620+
default 53 if MPSL_USE_EXTERNAL_CLOCK_CONTROL
621+
default KERNEL_INIT_PRIORITY_DEFAULT
622+
help
623+
This option configures the LL_SOFTDEVICE initialization priority level. The priority
624+
must be lower than the CONFIG_MPSL_INIT_PRIORITY due to dependency on the MPSL library.
625+
618626
endmenu
619627
endif # BT_LL_SOFTDEVICE

subsys/bluetooth/controller/hci_driver.c

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1523,10 +1523,15 @@ static int hci_driver_init(const struct device *dev)
15231523
return err;
15241524
}
15251525

1526+
#if defined(CONFIG_MPSL_USE_EXTERNAL_CLOCK_CONTROL)
1527+
BUILD_ASSERT(CONFIG_BT_LL_SOFTDEVICE_INIT_PRIORITY > CONFIG_MPSL_INIT_PRIORITY,
1528+
"MPSL must be initialized before SoftDevice Controller");
1529+
#endif /* CONFIG_MPSL_USE_EXTERNAL_CLOCK_CONTROL */
1530+
15261531
#define BT_HCI_CONTROLLER_INIT(inst) \
15271532
static struct hci_driver_data data_##inst; \
15281533
DEVICE_DT_INST_DEFINE(inst, hci_driver_init, NULL, &data_##inst, NULL, POST_KERNEL, \
1529-
CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &hci_driver_api)
1534+
CONFIG_BT_LL_SOFTDEVICE_INIT_PRIORITY, &hci_driver_api)
15301535

15311536
/* Only a single instance is supported */
15321537
BT_HCI_CONTROLLER_INIT(0)

subsys/mpsl/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,4 +18,8 @@ if (CONFIG_MPSL_USE_ZEPHYR_PM)
1818
add_subdirectory(pm)
1919
endif()
2020

21+
if (CONFIG_MPSL_USE_EXTERNAL_CLOCK_CONTROL)
22+
add_subdirectory(clock_ctrl)
23+
endif()
24+
2125
add_subdirectory_ifdef(CONFIG_MPSL_PIN_DEBUG pin_debug)

subsys/mpsl/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ rsource "cx/Kconfig"
2020
rsource "init/Kconfig"
2121
rsource "pin_debug/Kconfig"
2222
rsource "pm/Kconfig"
23+
rsource "clock_ctrl/Kconfig"
2324

2425
endif # !MPSL_FEM_ONLY
2526

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
#
2+
# Copyright (c) 2025 Nordic Semiconductor ASA
3+
#
4+
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
#
6+
7+
zephyr_library()
8+
9+
zephyr_library_sources(mpsl_clock_ctrl.c)

subsys/mpsl/clock_ctrl/Kconfig

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
#
2+
# Copyright (c) 2025 Nordic Semiconductor ASA
3+
#
4+
# SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
#
6+
7+
config MPSL_USE_EXTERNAL_CLOCK_CONTROL
8+
bool "Use external clock control [EXPERIMENTAL]"
9+
depends on MPSL
10+
select CLOCK_CONTROL
11+
default y if SOC_SERIES_NRF54HX
12+
select EXPERIMENTAL if SOC_SERIES_NRF52X || SOC_SERIES_NRF53X
13+
help
14+
This option configures MPSL to use an external clock driver, and
15+
not the clock driver provided as part of the MPSL library.
Lines changed: 234 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,234 @@
1+
/*
2+
* Copyright (c) 2025 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
*/
6+
7+
#include <zephyr/kernel.h>
8+
#include <zephyr/drivers/clock_control.h>
9+
#include <zephyr/drivers/clock_control/nrf_clock_control.h>
10+
11+
#if defined(CONFIG_CLOCK_CONTROL_NRF)
12+
#include <nrfx_clock.h>
13+
#endif /* CONFIG_CLOCK_CONTROL_NRF */
14+
15+
#include <mpsl_clock.h>
16+
#include "mpsl_clock_ctrl.h"
17+
18+
/* Variable shared for nrf and nrf2 clock control */
19+
static atomic_t m_hfclk_refcnt;
20+
21+
/* Type use to get information about a clock request status */
22+
struct clock_onoff_state {
23+
struct onoff_client cli;
24+
atomic_t m_clk_ready;
25+
atomic_t m_clk_refcnt;
26+
struct k_sem sem;
27+
int clk_req_rsp;
28+
};
29+
30+
static struct clock_onoff_state m_lfclk_state;
31+
32+
static int32_t m_lfclk_release(void);
33+
34+
#define MPSL_LFCLK_REQUEST_WAIT_TIMEOUT_MS 1000
35+
36+
/** @brief LFCLK request callback.
37+
*
38+
* The callback function provided to clock control to notify about LFCLK request being finished.
39+
*/
40+
static void lfclk_request_cb(struct onoff_manager *mgr, struct onoff_client *cli, uint32_t state,
41+
int res)
42+
{
43+
struct clock_onoff_state *clock_state = CONTAINER_OF(cli, struct clock_onoff_state, cli);
44+
45+
clock_state->clk_req_rsp = res;
46+
k_sem_give(&clock_state->sem);
47+
}
48+
49+
/** @brief Wait for LFCLK to be ready.
50+
*
51+
* The function can time out if there is no response from clock control drvier until
52+
* MPSL_LFCLK_REQUEST_WAIT_TIMEOUT_MS.
53+
*
54+
* @note For nRF54H SoC series waiting for LFCLK can't block the system work queue. The nrf2 clock
55+
* control driver can return -TIMEDOUT due not handled response from sysctrl.
56+
*
57+
* @retval 0 LFCLK is ready.
58+
* @retval -NRF_EINVAL There were no LFCLK request.
59+
* @retval -NRF_EFAULT LFCLK request failed.
60+
*/
61+
static int32_t m_lfclk_wait(void)
62+
{
63+
int32_t err;
64+
65+
if (atomic_get(&m_lfclk_state.m_clk_ready) == (atomic_val_t) true) {
66+
return 0;
67+
}
68+
69+
/* Check if lfclk has been requested */
70+
if (atomic_get(&m_lfclk_state.m_clk_refcnt) <= (atomic_val_t)0) {
71+
return -NRF_EINVAL;
72+
}
73+
74+
/* Wait for response from clock control */
75+
err = k_sem_take(&m_lfclk_state.sem, K_MSEC(MPSL_LFCLK_REQUEST_WAIT_TIMEOUT_MS));
76+
if (err < 0) {
77+
/* Do a gracefull cancel of the request, the function release does this
78+
* as well as and relase.
79+
*/
80+
(void)m_lfclk_release();
81+
82+
return -NRF_EFAULT;
83+
}
84+
85+
if (m_lfclk_state.clk_req_rsp < 0) {
86+
__ASSERT(false, "LFCLK could not be started, reason: %d",
87+
m_lfclk_state.clk_req_rsp);
88+
/* Possible failure reasons:
89+
* # -ERRTIMEDOUT - nRFS service timeout
90+
* # -EIO - nRFS service error
91+
* # -ENXIO - request rejected
92+
* All these mean failure for MPSL.
93+
*/
94+
return -NRF_EFAULT;
95+
}
96+
97+
atomic_set(&m_lfclk_state.m_clk_ready, (atomic_val_t) true);
98+
99+
return 0;
100+
}
101+
102+
#if defined(CONFIG_CLOCK_CONTROL_NRF)
103+
104+
static void m_lfclk_calibration_start(void)
105+
{
106+
if (IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_DRIVER_CALIBRATION)) {
107+
z_nrf_clock_calibration_force_start();
108+
}
109+
}
110+
111+
static bool m_lfclk_calibration_is_enabled(void)
112+
{
113+
if (IS_ENABLED(CONFIG_CLOCK_CONTROL_NRF_DRIVER_CALIBRATION)) {
114+
return true;
115+
} else {
116+
return false;
117+
}
118+
}
119+
120+
static int32_t m_lfclk_request(void)
121+
{
122+
struct onoff_manager *mgr = z_nrf_clock_control_get_onoff(CLOCK_CONTROL_NRF_SUBSYS_LF);
123+
int32_t err;
124+
125+
sys_notify_init_callback(&m_lfclk_state.cli.notify, lfclk_request_cb);
126+
(void)k_sem_init(&m_lfclk_state.sem, 0, 1);
127+
128+
err = onoff_request(mgr, &m_lfclk_state.cli);
129+
if (err < 0) {
130+
return err;
131+
}
132+
133+
atomic_inc(&m_lfclk_state.m_clk_refcnt);
134+
135+
return 0;
136+
}
137+
138+
static int32_t m_lfclk_release(void)
139+
{
140+
struct onoff_manager *mgr = z_nrf_clock_control_get_onoff(CLOCK_CONTROL_NRF_SUBSYS_LF);
141+
int32_t err;
142+
143+
/* In case there is other ongoing request, cancel it. */
144+
err = onoff_cancel_or_release(mgr, &m_lfclk_state.cli);
145+
if (err < 0) {
146+
return err;
147+
}
148+
149+
atomic_dec(&m_lfclk_state.m_clk_refcnt);
150+
151+
return 0;
152+
}
153+
154+
static void m_hfclk_request(void)
155+
{
156+
/* The z_nrf_clock_bt_ctlr_hf_request doesn't count references to HFCLK,
157+
* it is caller responsibility handle requests and releases counting.
158+
*/
159+
if (atomic_inc(&m_hfclk_refcnt) > (atomic_val_t)0) {
160+
return;
161+
}
162+
163+
z_nrf_clock_bt_ctlr_hf_request();
164+
}
165+
166+
static void m_hfclk_release(void)
167+
{
168+
/* The z_nrf_clock_bt_ctlr_hf_request doesn't count references to HFCLK,
169+
* it is caller responsibility to not release the clock if there is
170+
* other request pending.
171+
*/
172+
if (atomic_get(&m_hfclk_refcnt) < (atomic_val_t)1) {
173+
LOG_WRN("Mismatch between HFCLK request/release");
174+
return;
175+
}
176+
177+
if (atomic_dec(&m_hfclk_refcnt) > (atomic_val_t)1) {
178+
return;
179+
}
180+
181+
z_nrf_clock_bt_ctlr_hf_release();
182+
}
183+
184+
static bool m_hfclk_is_running(void)
185+
{
186+
if (atomic_get(&m_hfclk_refcnt) > (atomic_val_t)0) {
187+
nrf_clock_hfclk_t type;
188+
189+
unsigned int key = irq_lock();
190+
191+
(void)nrfx_clock_is_running(NRF_CLOCK_DOMAIN_HFCLK, &type);
192+
193+
irq_unlock(key);
194+
195+
return ((type == NRF_CLOCK_HFCLK_HIGH_ACCURACY) ? true : false);
196+
}
197+
198+
return false;
199+
}
200+
201+
#else
202+
#error "Unsupported clock control"
203+
#endif /* CONFIG_CLOCK_CONTROL_NRF */
204+
205+
static mpsl_clock_lfclk_ctrl_source_t m_nrf_lfclk_ctrl_data = {
206+
.lfclk_wait = m_lfclk_wait,
207+
.lfclk_calibration_start = m_lfclk_calibration_start,
208+
.lfclk_calibration_is_enabled = m_lfclk_calibration_is_enabled,
209+
.lfclk_request = m_lfclk_request,
210+
.lfclk_release = m_lfclk_release,
211+
#if defined(CONFIG_CLOCK_CONTROL_NRF_ACCURACY)
212+
.accuracy_ppm = CONFIG_CLOCK_CONTROL_NRF_ACCURACY,
213+
#else
214+
.accuracy_ppm = MPSL_LFCLK_ACCURACY_PPM,
215+
#endif /* CONFIG_CLOCK_CONTROL_NRF_ACCURACY */
216+
.skip_wait_lfclk_started = IS_ENABLED(CONFIG_SYSTEM_CLOCK_NO_WAIT)
217+
};
218+
219+
static mpsl_clock_hfclk_ctrl_source_t m_nrf_hfclk_ctrl_data = {
220+
.hfclk_request = m_hfclk_request,
221+
.hfclk_release = m_hfclk_release,
222+
.hfclk_is_running = m_hfclk_is_running,
223+
.startup_time_us = CONFIG_MPSL_HFCLK_LATENCY
224+
};
225+
226+
int32_t mpsl_clock_ctrl_init(void)
227+
{
228+
return mpsl_clock_ctrl_source_register(&m_nrf_lfclk_ctrl_data, &m_nrf_hfclk_ctrl_data);
229+
}
230+
231+
int32_t mpsl_clock_ctrl_uninit(void)
232+
{
233+
return mpsl_clock_ctrl_source_unregister();
234+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright (c) 2025 Nordic Semiconductor ASA
3+
*
4+
* SPDX-License-Identifier: LicenseRef-Nordic-5-Clause
5+
*/
6+
7+
#ifndef MPSL_CLOCK_CTRL_H__
8+
#define MPSL_CLOCK_CTRL_H__
9+
10+
#ifdef __cplusplus
11+
extern "C" {
12+
#endif
13+
14+
/** @brief Initialize MPSL integration with NRF clock control
15+
*
16+
* @retval 0 MPSL clock integration initialized successfully.
17+
* @retval -NRF_EPERM Clock control is already initialized.
18+
* @retval -NRF_EINVAL Invalid parameters supplied.
19+
*/
20+
int32_t mpsl_clock_ctrl_init(void);
21+
22+
/** @brief Uninitialize MPSL integration with NRF clock control
23+
*
24+
* @retval 0 MPSL clock was uninitialized successfully.
25+
* @retval -NRF_EPERM MPSL was not initialized before the call.
26+
*/
27+
int32_t mpsl_clock_ctrl_uninit(void);
28+
29+
#ifdef __cplusplus
30+
}
31+
#endif
32+
33+
#endif /* MPSL_CLOCK_CTRL_H__ */

subsys/mpsl/init/Kconfig

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ config MPSL_DYNAMIC_INTERRUPTS
5050
config MPSL_TRIGGER_IPC_TASK_ON_RTC_START
5151
bool "Trigger an IPC task when the RTC starts"
5252
depends on SOC_NRF5340_CPUNET
53+
depends on CLOCK_CONTROL_MPSL
5354
help
5455
This option configures MPSL to trigger an IPC task at the time the
5556
RTC starts. This can be used for synchronizing time betwen the
@@ -98,13 +99,22 @@ config MPSL_HFCLK_LATENCY
9899

99100
config MPSL_CALIBRATION_PERIOD
100101
int "Calibration callback period in milliseconds"
101-
depends on (SOC_SERIES_NRF54LX || CLOCK_CONTROL_NRF_K32SRC_RC_CALIBRATION) && !SOC_SERIES_NRF54HX
102+
depends on CLOCK_CONTROL_MPSL && (SOC_SERIES_NRF54LX || CLOCK_CONTROL_NRF_K32SRC_RC_CALIBRATION)
102103
default CLOCK_CONTROL_NRF_CALIBRATION_PERIOD if CLOCK_CONTROL_NRF_K32SRC_RC_CALIBRATION
103104
default 4000
104105
help
105106
This configuration means how often the calibration callback to mpsl is called.
106107
On 54L, this still needs to be called even if LFRC is not used.
107108

109+
config MPSL_INIT_PRIORITY
110+
int
111+
default 52 if MPSL_USE_EXTERNAL_CLOCK_CONTROL
112+
default KERNEL_INIT_PRIORITY_DEFAULT
113+
help
114+
This option configures MPSL system init priority level. For nRF54H SoC series the priority
115+
must be lower than CONFIG_NRFS_BACKEND_IPC_SERVICE_INIT_PRIO. The nrf2 clock control depends
116+
on the nRFS backend.
117+
108118
module=MPSL
109119
module-str=MPSL
110120
source "${ZEPHYR_BASE}/subsys/logging/Kconfig.template.log_config"

0 commit comments

Comments
 (0)