1414#include <mram_latency.h>
1515#endif /* CONFIG_MPSL_PM_USE_MRAM_LATENCY_SERVICE */
1616
17- #include <mpsl/mpsl_pm_utils.h>
17+ #include <mpsl/mpsl_work.h>
18+ #include "mpsl_pm_utils.h"
1819
1920LOG_MODULE_REGISTER (mpsl_pm_utils , CONFIG_MPSL_LOG_LEVEL );
2021
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)
2824
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 ;
3242static 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 ;
3447
3548#if defined(CONFIG_MPSL_PM_USE_MRAM_LATENCY_SERVICE )
3649#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
4053
4154static ATOMIC_DEFINE (m_low_latency_req_state , LOW_LATENCY_ATOMIC_BITS_NUM ) ;
4255/* 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)
5669
5770static void m_register_event (void )
5871{
59- mpsl_pm_params_t params = {0 };
72+ mpsl_pm_params_t params = {0 };
6073 bool pm_param_valid = mpsl_pm_params_get (& params );
6174
6275 if (m_pm_prev_flag_value == params .cnt_flag ) {
@@ -130,7 +143,7 @@ static void m_register_latency(void)
130143#if defined(CONFIG_MPSL_PM_USE_MRAM_LATENCY_SERVICE )
131144 m_mram_low_latency_release ();
132145#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 );
134147#if defined(CONFIG_MPSL_PM_USE_MRAM_LATENCY_SERVICE )
135148 atomic_clear_bit (m_low_latency_req_state , LOW_LATENCY_PM_BIT );
136149#endif /* CONFIG_MPSL_PM_USE_MRAM_LATENCY_SERVICE*/
@@ -201,21 +214,88 @@ static void m_mram_low_latency_release(void)
201214}
202215#endif /* CONFIG_MPSL_PM_USE_MRAM_LATENCY_SERVICE */
203216
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+
204224void mpsl_pm_utils_work_handler (void )
205225{
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+ }
208248}
209249
210- void mpsl_pm_utils_init (void )
250+ int32_t mpsl_pm_utils_init (void )
211251{
212252 mpsl_pm_params_t params = {0 };
213253
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 ;
216260
217261 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 );
219264 m_pm_prev_flag_value = params .cnt_flag ;
220265 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 ;
221301}
0 commit comments