14
14
#include <mram_latency.h>
15
15
#endif /* CONFIG_MPSL_PM_USE_MRAM_LATENCY_SERVICE */
16
16
17
- #include <mpsl/mpsl_pm_utils.h>
17
+ #include <mpsl/mpsl_work.h>
18
+ #include "mpsl_pm_utils.h"
18
19
19
20
LOG_MODULE_REGISTER (mpsl_pm_utils , CONFIG_MPSL_LOG_LEVEL );
20
21
21
- /* These constants must be updated once the Zephyr PM Policy API is updated
22
- * to handle low latency events. Ideally, the Policy API should be changed to use
23
- * absolute time instead of relative time. This would remove the need for safety
24
- * margins and allow optimal power savings.
25
- */
26
- #define TIME_TO_REGISTER_EVENT_IN_ZEPHYR_US 1000
27
- #define PM_MAX_LATENCY_HCI_COMMANDS_US 499999
22
+ #define NO_RADIO_EVENT_PERIOD_LATENCY_US CONFIG_MPSL_PM_NO_RADIO_EVENT_PERIOD_LATENCY_US
23
+ #define UNINIT_WORK_WAIT_TIMEOUT_MS K_MSEC(CONFIG_MPSL_PM_UNINIT_WORK_WAIT_TIME_MS)
28
24
29
- static uint8_t m_pm_prev_flag_value ;
30
- static bool m_pm_event_is_registered ;
31
- static uint32_t m_prev_lat_value_us ;
25
+ /* All MPSL PM event and latency actions are triggered inside the library.
26
+ * The uninitialization may be started outside of the library thgough public API.
27
+ * To avoid interference with other MPSL work items we need dedicated work
28
+ * item for uninitialization purpose.
29
+ */
30
+ static void m_pm_uninit_work_handler (struct k_work * work );
31
+ static K_WORK_DELAYABLE_DEFINE (m_pm_uninit_work , m_pm_uninit_work_handler ) ;
32
+
33
+ enum MPLS_PM_STATE {
34
+ MPSL_PM_UNINITIALIZED ,
35
+ MPSL_PM_UNINITIALIZING ,
36
+ MPSL_PM_INITIALIZED
37
+ };
38
+
39
+ static uint8_t m_pm_prev_flag_value ;
40
+ static bool m_pm_event_is_registered ;
41
+ static uint32_t m_prev_lat_value_us ;
32
42
static struct pm_policy_latency_request m_latency_req ;
33
- static struct pm_policy_event m_evt ;
43
+ static struct pm_policy_event m_evt ;
44
+
45
+ static atomic_t m_pm_state = (atomic_val_t )MPSL_PM_UNINITIALIZED ;
46
+ struct k_sem m_uninit_wait_sem ;
34
47
35
48
#if defined(CONFIG_MPSL_PM_USE_MRAM_LATENCY_SERVICE )
36
49
#define LOW_LATENCY_ATOMIC_BITS_NUM 2
37
- #define LOW_LATENCY_PM_BIT 0
38
- #define LOW_LATENCY_MRAM_BIT 0
39
- #define LOW_LATENCY_BITS_MASK 0x3
50
+ #define LOW_LATENCY_PM_BIT 0
51
+ #define LOW_LATENCY_MRAM_BIT 0
52
+ #define LOW_LATENCY_BITS_MASK 0x3
40
53
41
54
static ATOMIC_DEFINE (m_low_latency_req_state , LOW_LATENCY_ATOMIC_BITS_NUM ) ;
42
55
/* Variable must be global to use it in on-off service cancel or release API */
@@ -56,7 +69,7 @@ static void m_update_latency_request(uint32_t lat_value_us)
56
69
57
70
static void m_register_event (void )
58
71
{
59
- mpsl_pm_params_t params = {0 };
72
+ mpsl_pm_params_t params = {0 };
60
73
bool pm_param_valid = mpsl_pm_params_get (& params );
61
74
62
75
if (m_pm_prev_flag_value == params .cnt_flag ) {
@@ -130,7 +143,7 @@ static void m_register_latency(void)
130
143
#if defined(CONFIG_MPSL_PM_USE_MRAM_LATENCY_SERVICE )
131
144
m_mram_low_latency_release ();
132
145
#endif /* CONFIG_MPSL_PM_USE_MRAM_LATENCY_SERVICE */
133
- m_update_latency_request (PM_MAX_LATENCY_HCI_COMMANDS_US );
146
+ m_update_latency_request (NO_RADIO_EVENT_PERIOD_LATENCY_US );
134
147
#if defined(CONFIG_MPSL_PM_USE_MRAM_LATENCY_SERVICE )
135
148
atomic_clear_bit (m_low_latency_req_state , LOW_LATENCY_PM_BIT );
136
149
#endif /* CONFIG_MPSL_PM_USE_MRAM_LATENCY_SERVICE*/
@@ -201,21 +214,88 @@ static void m_mram_low_latency_release(void)
201
214
}
202
215
#endif /* CONFIG_MPSL_PM_USE_MRAM_LATENCY_SERVICE */
203
216
217
+ static void m_pm_uninit_work_handler (struct k_work * work )
218
+ {
219
+ ARG_UNUSED (work );
220
+
221
+ mpsl_pm_utils_work_handler ();
222
+ }
223
+
204
224
void mpsl_pm_utils_work_handler (void )
205
225
{
206
- m_register_event ();
207
- m_register_latency ();
226
+ enum MPLS_PM_STATE pm_state = (enum MPLS_PM_STATE )atomic_get (& m_pm_state );
227
+
228
+ if (pm_state == MPSL_PM_INITIALIZED ) {
229
+ m_register_event ();
230
+ m_register_latency ();
231
+ } else if (pm_state == MPSL_PM_UNINITIALIZING ) {
232
+
233
+ /* The uninitialization is handled by all MPSL work items as well as by dedicated
234
+ * uninit work item. In case regular MPSL work item cleans MPSL PM the uninit
235
+ * work queue will not do anything.
236
+ */
237
+ pm_policy_latency_request_remove (& m_latency_req );
238
+ pm_policy_event_unregister (& m_evt );
239
+
240
+ /* The MPSL PM status is updated here to make the code unit testable.
241
+ * There is no work queue when running UTs, so the mpsl_pm_utils_work_handler()
242
+ * is manualy executed after mpsl_pm_utils_uninit() returns.
243
+ */
244
+ atomic_set (& m_pm_state , (atomic_val_t )MPSL_PM_UNINITIALIZED );
245
+
246
+ k_sem_give (& m_uninit_wait_sem );
247
+ }
208
248
}
209
249
210
- void mpsl_pm_utils_init (void )
250
+ int32_t mpsl_pm_utils_init (void )
211
251
{
212
252
mpsl_pm_params_t params = {0 };
213
253
214
- pm_policy_latency_request_add (& m_latency_req , PM_MAX_LATENCY_HCI_COMMANDS_US );
215
- m_prev_lat_value_us = PM_MAX_LATENCY_HCI_COMMANDS_US ;
254
+ if (atomic_get (& m_pm_state ) != (atomic_val_t )MPSL_PM_UNINITIALIZED ) {
255
+ return - NRF_EPERM ;
256
+ }
257
+
258
+ pm_policy_latency_request_add (& m_latency_req , NO_RADIO_EVENT_PERIOD_LATENCY_US );
259
+ m_prev_lat_value_us = NO_RADIO_EVENT_PERIOD_LATENCY_US ;
216
260
217
261
mpsl_pm_init ();
218
- mpsl_pm_params_get (& params );
262
+ /* On init there should be no update from high-prio, returned value can be ignored */
263
+ (void )mpsl_pm_params_get (& params );
219
264
m_pm_prev_flag_value = params .cnt_flag ;
220
265
m_pm_event_is_registered = false;
266
+
267
+ atomic_set (& m_pm_state , (atomic_val_t )MPSL_PM_INITIALIZED );
268
+
269
+ return 0 ;
270
+ }
271
+
272
+ int32_t mpsl_pm_utils_uninit (void )
273
+ {
274
+ int err ;
275
+
276
+ if (atomic_get (& m_pm_state ) != (atomic_val_t )MPSL_PM_INITIALIZED ) {
277
+ return - NRF_EPERM ;
278
+ }
279
+
280
+ mpsl_pm_uninit ();
281
+ atomic_set (& m_pm_state , (atomic_val_t )MPSL_PM_UNINITIALIZING );
282
+ /* In case there is any pended MPSL work item that was not handled, a dedicated
283
+ * work item is used to remove PM policy event and unregister latency request.
284
+ */
285
+ (void )k_sem_init (& m_uninit_wait_sem , 0 , 1 );
286
+
287
+ mpsl_work_reschedule (& m_pm_uninit_work , K_NO_WAIT );
288
+
289
+ /* Wait for completion of the uninit work item to make sure user can re-initialize
290
+ * MPSL PM after return.
291
+ */
292
+ err = k_sem_take (& m_uninit_wait_sem , UNINIT_WORK_WAIT_TIMEOUT_MS );
293
+ if (err == - EAGAIN ) {
294
+ return - NRF_ETIMEDOUT ;
295
+ } else if (err != 0 ) {
296
+ __ASSERT (false, "MPSL PM uninit failed to complete: %d" , err );
297
+ return - NRF_EFAULT ;
298
+ }
299
+
300
+ return 0 ;
221
301
}
0 commit comments