1 /* -------------------------------------------------------------------------- */
2 /*                           Copyright 2021-2024 NXP                          */
3 /*                            All rights reserved.                            */
4 /*                    SPDX-License-Identifier: BSD-3-Clause                   */
5 /* -------------------------------------------------------------------------- */
6 
7 /* -------------------------------------------------------------------------- */
8 /*                                  Includes                                  */
9 /* -------------------------------------------------------------------------- */
10 
11 #include <stdarg.h>
12 #include "fwk_platform.h"
13 #include "fwk_platform_ics.h"
14 #if defined(FPGA_SUPPORT) && (FPGA_SUPPORT == 1)
15 #include "fwk_platform_fpga.h"
16 #endif
17 
18 #include "fsl_os_abstraction.h"
19 #include "fsl_adapter_rpmsg.h"
20 #include "fsl_spc.h"
21 
22 #include "rpmsg_platform.h"
23 
24 #include "mcmgr_imu_internal.h"
25 
26 /* -------------------------------------------------------------------------- */
27 /*                               Private macros                               */
28 /* -------------------------------------------------------------------------- */
29 
30 /*! @brief XTAL 32Mhz clock source start up timeout */
31 #ifndef FWK_PLATFORM_XTAL32M_STARTUP_TIMEOUT
32 #define FWK_PLATFORM_XTAL32M_STARTUP_TIMEOUT 1000000
33 #endif /* FWK_PLATFORM_XTAL32M_STARTUP_TIMEOUT */
34 
35 /*! @brief Delay from the 32MHz wake up of the LL to wake up the radio domain in number of 30.5us period */
36 #if !defined(BOARD_RADIO_DOMAIN_WAKE_UP_DELAY)
37 #define BOARD_RADIO_DOMAIN_WAKE_UP_DELAY 0x10U
38 #endif
39 
40 /*! @brief Remote active request timeout */
41 #ifndef FWK_PLATFORM_ACTIVE_REQ_TIMEOUT_US
42 #define FWK_PLATFORM_ACTIVE_REQ_TIMEOUT_US 10000U
43 #endif /* FWK_PLATFORM_ACTIVE_REQ_TIMEOUT_US */
44 
45 /* Raise error with status update , shift previous status by 4 bits and OR with new error code.
46  * the returned status will be negative
47  * Note: this macros is defined in sss_crypto.h, we need to undef it to make sure we use our implementation */
48 #undef RAISE_ERROR
49 #define RAISE_ERROR(__st, __error_code) -(int)((uint32_t)(((uint32_t)(__st) << 4) | (uint32_t)(__error_code)));
50 
51 /* -------------------------------------------------------------------------- */
52 /*                         Private memory declarations                        */
53 /* -------------------------------------------------------------------------- */
54 
55 static int nbu_init = 0;
56 
57 /****************** LOWPOWER ***********************/
58 /* Number of request for CM3 to remain active */
59 static int8_t active_request_nb = 0;
60 
61 extern PLATFORM_ErrorCallback_t pfPlatformErrorCallback;
62 PLATFORM_ErrorCallback_t        pfPlatformErrorCallback = (void *)0;
63 
64 /* -------------------------------------------------------------------------- */
65 /*                              Private functions                              */
66 /* -------------------------------------------------------------------------- */
67 
u64ReadTimeStamp(void)68 static uint64_t u64ReadTimeStamp(void)
69 {
70     uint32_t reg_l;
71     uint32_t reg_h;
72     uint32_t regPrimask = DisableGlobalIRQ();
73     /* A complete read operation should include both TSTMR LOW and HIGH reads. If a HIGH read does not follow a LOW
74      * read, then any other Time Stamp value read will be locked at a fixed value. The TSTMR LOW read should occur
75      * first, followed by the TSTMR HIGH read.
76      * */
77     reg_l = TSTMR0->L;
78     __DMB();
79     reg_h = TSTMR0->H;
80 
81     EnableGlobalIRQ(regPrimask);
82 
83     return (uint64_t)reg_l | (((uint64_t)reg_h) << 32U);
84 }
85 /* -------------------------------------------------------------------------- */
86 /*                              Public functions                              */
87 /* -------------------------------------------------------------------------- */
PLATFORM_IsNbuStarted(void)88 int PLATFORM_IsNbuStarted(void)
89 {
90     return nbu_init;
91 }
PLATFORM_InitNbu(void)92 int PLATFORM_InitNbu(void)
93 {
94     int status = 0;
95 
96     if (nbu_init == 0)
97     {
98         uint32_t rfmc_ctrl;
99         int      cnt = 0;
100 
101         rfmc_ctrl = RFMC->RF2P4GHZ_CTRL;
102 
103         /* Enables BLE Power Controller on NBU side AND sets LP mode to DeepSleep */
104         rfmc_ctrl &= ~(RFMC_RF2P4GHZ_CTRL_LP_WKUP_DLY_MASK | RFMC_RF2P4GHZ_CTRL_LP_MODE_MASK);
105         rfmc_ctrl |=
106             (RFMC_RF2P4GHZ_CTRL_LP_MODE(0x3) | RFMC_RF2P4GHZ_CTRL_LP_WKUP_DLY(BOARD_RADIO_DOMAIN_WAKE_UP_DELAY) |
107              RFMC_RF2P4GHZ_CTRL_BLE_LP_EN(0x1U) | RFMC_RF2P4GHZ_CTRL_SFA_TRIG_EN(0x7U));
108 
109         RFMC->RF2P4GHZ_CTRL = rfmc_ctrl;
110         RFMC->RF2P4GHZ_TIMER |= RFMC_RF2P4GHZ_TIMER_TIM_EN(0x1U);
111 
112         /* Enabling BLE Power Controller (BLE_LP_EN) will automatically start the XTAL
113          * According to RM, we need to wait for the XTAL to be ready before accessing
114          * Radio domain ressources.
115          * Here, we make sure the XTAL is ready before releasing the CM3 from reset
116          * it will prevent any access issue when the CM3 is starting up.
117          * CM3 is released in HAL_RpmsgMcmgrInit */
118         while ((cnt++ < FWK_PLATFORM_XTAL32M_STARTUP_TIMEOUT) && ((RFMC->XO_STAT & RFMC_XO_STAT_XTAL_RDY_MASK) == 0U))
119         {
120             __ASM("NOP");
121         }
122 
123         if (cnt > FWK_PLATFORM_XTAL32M_STARTUP_TIMEOUT)
124         {
125             status = RAISE_ERROR(0, 1);
126         }
127         else
128         {
129             /* nbu initialization completed */
130             nbu_init = 1;
131         }
132     }
133     else
134     {
135         /* Initialization already done */
136         status = 1;
137     }
138 
139     return status;
140 }
141 
PLATFORM_InitMulticore(void)142 int PLATFORM_InitMulticore(void)
143 {
144     int                status = 0;
145     hal_rpmsg_status_t rpmsg_status;
146     static bool        first_call = true;
147     if (first_call)
148     {
149         /* Write in SMU2 the static shared memory config that we have for NBU to use it */
150         platform_set_static_shmem_config();
151     }
152 
153     first_call = false;
154 
155     /* Start CM3 core and Initializes the RPMSG adapter module for dual core communication */
156     rpmsg_status = HAL_RpmsgMcmgrInit();
157     if (rpmsg_status != kStatus_HAL_RpmsgSuccess)
158     {
159         status = RAISE_ERROR(rpmsg_status, 1);
160         assert(0);
161     }
162 
163 #if defined(FPGA_SUPPORT) && (FPGA_SUPPORT == 1)
164     PLATFORM_InitRadio();
165 #endif
166 
167     return status;
168 }
169 
170 /* get 4 words of information that uniquely identifies the MCU */
PLATFORM_GetMCUUid(uint8_t * aOutUid16B,uint8_t * pOutLen)171 void PLATFORM_GetMCUUid(uint8_t *aOutUid16B, uint8_t *pOutLen)
172 {
173     uint32_t uid[4] = {0};
174 
175     /* Get the MCU uid */
176     uid[0] = MSCM->UID[0];
177     uid[1] = MSCM->UID[1];
178     uid[2] = MSCM->UID[2];
179     uid[3] = MSCM->UID[3];
180 
181     memcpy(aOutUid16B, (uint8_t *)uid, sizeof(uid));
182     /* Get the uid length */
183     *pOutLen = (uint8_t)sizeof(uid);
184 
185     return;
186 }
187 
PLATFORM_GetTimeStamp(void)188 uint64_t PLATFORM_GetTimeStamp(void)
189 {
190     /* u64ReadTimeStamp mimics TSTMR_ReadTimeStamp(TSTMR0) but copied to avoid dependency
191      * with fsl_tstmr.h not present in include path
192      */
193     return u64ReadTimeStamp();
194 }
195 
PLATFORM_GetMaxTimeStamp(void)196 uint64_t PLATFORM_GetMaxTimeStamp(void)
197 {
198     /* Max timestamp counter value (56 bits)
199      * No conversion to us is needed since the timestamp timer runs off the 1 MHz clock*/
200     return ((uint64_t)0xFFFFFFFFFFFFFFU);
201 }
202 
PLATFORM_IsTimeoutExpired(uint64_t timestamp,uint64_t delayUs)203 bool PLATFORM_IsTimeoutExpired(uint64_t timestamp, uint64_t delayUs)
204 {
205     uint64_t now, duration;
206 
207     now = PLATFORM_GetTimeStamp();
208 
209     if (now < timestamp)
210     {
211         /* Handle counter wrapping */
212         duration = PLATFORM_GetMaxTimeStamp() - timestamp + now;
213     }
214     else
215     {
216         duration = now - timestamp;
217     }
218     return (duration >= delayUs);
219 }
220 
PLATFORM_WaitTimeout(uint64_t timestamp,uint64_t delayUs)221 void PLATFORM_WaitTimeout(uint64_t timestamp, uint64_t delayUs)
222 {
223     while (!(PLATFORM_IsTimeoutExpired(timestamp, delayUs)))
224     {
225     }
226 }
227 
PLATFORM_Delay(uint64_t delayUs)228 void PLATFORM_Delay(uint64_t delayUs)
229 {
230     /* PLATFORM_Delay() is similar to PLATFORM_WaitTimeout() but timestamp is taken right now */
231     PLATFORM_WaitTimeout(PLATFORM_GetTimeStamp(), delayUs);
232 }
233 
PLATFORM_RemoteActiveReq(void)234 void PLATFORM_RemoteActiveReq(void)
235 {
236     OSA_InterruptDisable();
237 
238     if (active_request_nb == 0)
239     {
240         uint32_t rfmc_ctrl    = RFMC->RF2P4GHZ_CTRL;
241         bool     remote_in_lp = false;
242         uint64_t timestamp    = PLATFORM_GetTimeStamp();
243 
244         /* Set lp wakeup delay to 0 to reduce time of execution on host side, NBU will wait XTAL to be ready before
245          * starting execution */
246         uint32_t lp_wakeup_delay;
247         lp_wakeup_delay = (rfmc_ctrl & RFMC_RF2P4GHZ_CTRL_LP_WKUP_DLY_MASK) >> RFMC_RF2P4GHZ_CTRL_LP_WKUP_DLY_SHIFT;
248         rfmc_ctrl &= ~(RFMC_RF2P4GHZ_CTRL_LP_WKUP_DLY_MASK);
249         rfmc_ctrl |= RFMC_RF2P4GHZ_CTRL_LP_WKUP_DLY(0U);
250         RFMC->RF2P4GHZ_CTRL = rfmc_ctrl;
251 
252         /* CM3 writes to WKUP_TIME register to notify CM33 it's going to low
253          * power, this is a software protocol to sync both cores */
254         while ((RFMC->RF2P4GHZ_MAN2 & RFMC_RF2P4GHZ_MAN2_WKUP_TIME_MASK) != 0U)
255         {
256             /* CM3 started low power entry, to workaround HW issues, we need to
257              * wait for the radio to fully enter low power before waking it up */
258             if ((RFMC->RF2P4GHZ_STAT & RFMC_RF2P4GHZ_STAT_BLE_STATE_MASK) == RFMC_RF2P4GHZ_STAT_BLE_STATE(0x2U))
259             {
260                 /* Radio is in low power, we can exit the loop and wake it up */
261                 remote_in_lp = true;
262                 break;
263             }
264             /* Error callback set by PLATFORM_RegisterBleErrorCallback() */
265             if (PLATFORM_IsTimeoutExpired(timestamp, FWK_PLATFORM_ACTIVE_REQ_TIMEOUT_US) &&
266                 (pfPlatformErrorCallback != NULL))
267             {
268                 pfPlatformErrorCallback(PLATFORM_REMOTE_ACTIVE_REQ_ID, -1);
269                 break;
270             }
271         }
272 
273         rfmc_ctrl |= RFMC_RF2P4GHZ_CTRL_BLE_WKUP(0x1U);
274         RFMC->RF2P4GHZ_CTRL = rfmc_ctrl;
275 
276         __DSB();
277 
278         if (remote_in_lp == true)
279         {
280             /* Wake up time is around 5 periods of 32khz clock (160us)
281              * Adding a delay of 120us shouldn't impact waiting time, and will
282              * make sure the BLE_STATE is reliable */
283             PLATFORM_Delay(120U);
284         }
285 
286         timestamp = PLATFORM_GetTimeStamp();
287         /* Wait for the NBU to become active */
288         while ((RFMC->RF2P4GHZ_STAT & RFMC_RF2P4GHZ_STAT_BLE_STATE_MASK) != RFMC_RF2P4GHZ_STAT_BLE_STATE(0x1U))
289         {
290             __ASM("NOP");
291             /* Error callback set by PLATFORM_RegisterBleErrorCallback() */
292             if (PLATFORM_IsTimeoutExpired(timestamp, FWK_PLATFORM_ACTIVE_REQ_TIMEOUT_US) &&
293                 (pfPlatformErrorCallback != NULL))
294             {
295                 pfPlatformErrorCallback(PLATFORM_REMOTE_ACTIVE_REQ_ID, -2);
296                 break;
297             }
298         }
299         rfmc_ctrl |= RFMC_RF2P4GHZ_CTRL_LP_WKUP_DLY(lp_wakeup_delay);
300         RFMC->RF2P4GHZ_CTRL = rfmc_ctrl;
301     }
302     else
303     {
304         ;
305     }
306 
307     active_request_nb++;
308 
309     OSA_InterruptEnable();
310 
311 }
312 
PLATFORM_RemoteActiveRel(void)313 void PLATFORM_RemoteActiveRel(void)
314 {
315     OSA_InterruptDisable();
316 
317     assert(active_request_nb > 0);
318     active_request_nb--;
319 
320     if (active_request_nb == 0)
321     {
322         uint32_t rfmc_ctrl;
323         rfmc_ctrl = RFMC->RF2P4GHZ_CTRL;
324         rfmc_ctrl &= ~RFMC_RF2P4GHZ_CTRL_BLE_WKUP_MASK;
325         RFMC->RF2P4GHZ_CTRL = rfmc_ctrl;
326     }
327 
328     OSA_InterruptEnable();
329 }
330 
mcmgr_imu_remote_active_rel(void)331 void mcmgr_imu_remote_active_rel(void)
332 {
333     PLATFORM_RemoteActiveRel();
334 }
335 
mcmgr_imu_remote_active_req(void)336 void mcmgr_imu_remote_active_req(void)
337 {
338     PLATFORM_RemoteActiveReq();
339 }
340 
PLATFORM_SetNbuConstraintFrequency(PLATFORM_NbuConstraintFrequency_t freq_constraint)341 void PLATFORM_SetNbuConstraintFrequency(PLATFORM_NbuConstraintFrequency_t freq_constraint)
342 {
343     (void)PLATFORM_FwkSrvSendPacket(gFwkSrvNbuUpdateFrequencyConstraintFromHost, &freq_constraint,
344                                     (uint16_t)sizeof(freq_constraint));
345 }
346 
PLATFORM_RegisterErrorCallback(PLATFORM_ErrorCallback_t cb)347 void PLATFORM_RegisterErrorCallback(PLATFORM_ErrorCallback_t cb)
348 {
349     pfPlatformErrorCallback = cb;
350 }
351 
PLATFORM_ClearIoIsolationFromLowPower(void)352 int PLATFORM_ClearIoIsolationFromLowPower(void)
353 {
354     int ret = 0;
355     if ((SPC_CheckPowerDomainLowPowerRequest(SPC0, kSPC_PowerDomain0) == true) &&
356         (SPC_GetPowerDomainLowPowerMode(SPC0, kSPC_PowerDomain0) >= kSPC_PowerDownWithSysClockOff))
357     {
358         /* We need to release IO isolation when exiting from Power Down mode
359          * This is done here after all peripherals have been reinitialized, to
360          * avoid any glitch on IOs */
361         SPC_ClearPeriphIOIsolationFlag(SPC0);
362 
363         ret = 1;
364     }
365     return ret;
366 }
367