1 /*
2  * Copyright (c) 2017 - 2025, Nordic Semiconductor ASA
3  * All rights reserved.
4  *
5  * SPDX-License-Identifier: BSD-3-Clause
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright notice, this
11  *    list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * 3. Neither the name of the copyright holder nor the names of its
18  *    contributors may be used to endorse or promote products derived from this
19  *    software without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22  * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
25  * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
26  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31  * POSSIBILITY OF SUCH DAMAGE.
32  */
33 
34 #include <nrfx.h>
35 
36 #if NRFX_CHECK(NRFX_POWER_ENABLED)
37 
38 #include <nrfx_power.h>
39 
40 #if NRFX_CHECK(NRFX_CLOCK_ENABLED)
41 
42 #ifdef __cplusplus
43 extern "C" {
44 #endif
45 
46 extern bool nrfx_clock_irq_enabled;
47 extern void nrfx_clock_irq_handler(void);
48 
49 #ifdef __cplusplus
50 }
51 #endif
52 
53 #endif // NRFX_CHECK(NRFX_CLOCK_ENABLED)
54 
55 /**
56  * @internal
57  * @defgroup nrfx_power_internals POWER driver internals
58  * @ingroup nrfx_power
59  *
60  * Internal variables, auxiliary macros and functions of POWER driver.
61  * @{
62  */
63 
64 #if (NRF_POWER_HAS_CONST_LATENCY && NRF_POWER_HAS_LOW_POWER)
65 /** This variable is used as a reference counter of @ref nrfx_power_constlat_mode_request calls. */
66 static uint8_t m_power_mode_refs = 0;
67 #endif
68 
69 /**
70  * This variable is used to check whether common POWER_CLOCK common interrupt
71  * should be disabled or not if @ref nrfx_clock tries to disable the interrupt.
72  */
73 
74 bool nrfx_power_irq_enabled;
75 
76 /**
77  * @brief The initialization flag
78  */
79 
80 #define m_initialized nrfx_power_irq_enabled
81 
82 /**
83  * @brief The handler of power fail comparator warning event
84  */
85 static nrfx_power_pofwarn_event_handler_t m_pofwarn_handler;
86 
87 #if NRF_POWER_HAS_SLEEPEVT
88 /**
89  * @brief The handler of sleep event handler
90  */
91 static nrfx_power_sleep_event_handler_t m_sleepevt_handler;
92 #endif
93 
94 #if NRF_POWER_HAS_USBREG
95 /**
96  * @brief The handler of USB power events
97  */
98 static nrfx_power_usb_event_handler_t m_usbevt_handler;
99 #endif
100 
101 /** @} */
102 
nrfx_power_pof_handler_get(void)103 nrfx_power_pofwarn_event_handler_t nrfx_power_pof_handler_get(void)
104 {
105     return m_pofwarn_handler;
106 }
107 
108 #if NRF_POWER_HAS_USBREG
nrfx_power_usb_handler_get(void)109 nrfx_power_usb_event_handler_t nrfx_power_usb_handler_get(void)
110 {
111     return m_usbevt_handler;
112 }
113 #endif
114 
nrfx_power_init(nrfx_power_config_t const * p_config)115 nrfx_err_t nrfx_power_init(nrfx_power_config_t const * p_config)
116 {
117     NRFX_ASSERT(p_config);
118     if (m_initialized)
119     {
120         return NRFX_ERROR_ALREADY;
121     }
122 
123 #if NRF_POWER_HAS_DCDCEN_VDDH
124     nrf_power_dcdcen_vddh_set(NRF_POWER, p_config->dcdcenhv);
125 #elif defined(REGULATORS_PRESENT) && NRF_REGULATORS_HAS_VREG_HIGH
126     nrf_regulators_vreg_enable_set(NRF_REGULATORS, NRF_REGULATORS_VREG_HIGH, p_config->dcdcenhv);
127 #endif
128 
129 #if NRF_POWER_HAS_DCDCEN
130     nrf_power_dcdcen_set(NRF_POWER, p_config->dcdcen);
131 #elif defined(REGULATORS_PRESENT)
132     nrf_regulators_vreg_enable_set(NRF_REGULATORS, NRF_REGULATORS_VREG_MAIN, p_config->dcdcen);
133 #if !defined(NRF_TRUSTZONE_NONSECURE)
134     if (p_config->dcdcen && nrf53_errata_53())
135     {
136         *((volatile uint32_t *)0x50004728ul) = 0x1;
137     }
138 #endif
139 #endif // defined(REGULATORS_PRESENT)
140 
141     nrfx_power_clock_irq_init();
142 
143     m_initialized = true;
144     return NRFX_SUCCESS;
145 }
146 
147 
nrfx_power_uninit(void)148 void nrfx_power_uninit(void)
149 {
150     NRFX_ASSERT(m_initialized);
151 
152 #if NRFX_CHECK(NRFX_CLOCK_ENABLED)
153     if (!nrfx_clock_irq_enabled)
154 #endif
155     {
156 #if defined(NRF54L05_XXAA) || defined(NRF54L10_XXAA) || defined(NRF54L15_XXAA)
157         IRQn_Type irqn = CLOCK_POWER_IRQn;
158 #else
159         IRQn_Type irqn = nrfx_get_irq_number(NRF_POWER);
160 #endif
161         NRFX_IRQ_DISABLE(irqn);
162     }
163 #if NRFX_POWER_SUPPORTS_POFCON
164     nrfx_power_pof_uninit();
165 #endif
166 #if NRF_POWER_HAS_SLEEPEVT
167     nrfx_power_sleepevt_uninit();
168 #endif
169 #if NRF_POWER_HAS_USBREG || defined(USBREG_PRESENT)
170     nrfx_power_usbevt_uninit();
171 #endif
172     m_initialized = false;
173 }
174 
nrfx_power_init_check(void)175 bool nrfx_power_init_check(void)
176 {
177     return m_initialized;
178 }
179 
180 #if NRFX_POWER_SUPPORTS_POFCON
nrfx_power_pof_init(nrfx_power_pofwarn_config_t const * p_config)181 void nrfx_power_pof_init(nrfx_power_pofwarn_config_t const * p_config)
182 {
183     NRFX_ASSERT(p_config != NULL);
184 
185     nrfx_power_pof_uninit();
186 
187     if (p_config->handler != NULL)
188     {
189         m_pofwarn_handler = p_config->handler;
190     }
191 }
192 
nrfx_power_pof_enable(nrfx_power_pofwarn_config_t const * p_config)193 void nrfx_power_pof_enable(nrfx_power_pofwarn_config_t const * p_config)
194 {
195 #if NRF_POWER_HAS_POFCON
196     nrf_power_pofcon_set(NRF_POWER, true, p_config->thr);
197 #if NRF_POWER_HAS_POFCON_VDDH
198     nrf_power_pofcon_vddh_set(NRF_POWER, p_config->thrvddh);
199 #endif
200 #elif NRF_REGULATORS_HAS_POF
201     nrf_regulators_pof_config_t pof_config = {
202         .enable       = true,
203         .thr          = p_config->thr,
204 #if NRF_REGULATORS_HAS_POF_VDDH
205         .thr_vddh     = p_config->thrvddh,
206 #endif
207 #if NRF_REGULATORS_HAS_POF_WARN_DISABLE
208         .warn_disable = false,
209 #endif
210     };
211     nrf_regulators_pof_config_set(NRF_REGULATORS, &pof_config);
212 #endif
213 
214     if (m_pofwarn_handler != NULL)
215     {
216         nrf_power_int_enable(NRF_POWER, NRF_POWER_INT_POFWARN_MASK);
217     }
218 }
219 
nrfx_power_pof_disable(void)220 void nrfx_power_pof_disable(void)
221 {
222 #if NRF_POWER_HAS_POFCON
223     nrf_power_pofcon_set(NRF_POWER, false, NRF_POWER_POFTHR_V27);
224 #elif NRF_REGULATORS_HAS_POF
225     nrf_regulators_pof_config_t pof_config = {
226         .enable = false,
227         .thr    = NRF_REGULATORS_POF_THR_2V7,
228     };
229     nrf_regulators_pof_config_set(NRF_REGULATORS, &pof_config);
230 #endif
231     nrf_power_int_disable(NRF_POWER, NRF_POWER_INT_POFWARN_MASK);
232 }
233 
nrfx_power_pof_uninit(void)234 void nrfx_power_pof_uninit(void)
235 {
236     m_pofwarn_handler = NULL;
237 }
238 #endif // NRFX_POWER_SUPPORTS_POFCON
239 
240 #if NRF_POWER_HAS_SLEEPEVT
nrfx_power_sleepevt_init(nrfx_power_sleepevt_config_t const * p_config)241 void nrfx_power_sleepevt_init(nrfx_power_sleepevt_config_t const * p_config)
242 {
243     NRFX_ASSERT(p_config != NULL);
244 
245     nrfx_power_sleepevt_uninit();
246     if (p_config->handler != NULL)
247     {
248         m_sleepevt_handler = p_config->handler;
249     }
250 }
251 
nrfx_power_sleepevt_enable(nrfx_power_sleepevt_config_t const * p_config)252 void nrfx_power_sleepevt_enable(nrfx_power_sleepevt_config_t const * p_config)
253 {
254     uint32_t enmask = 0;
255     if (p_config->en_enter)
256     {
257         enmask |= NRF_POWER_INT_SLEEPENTER_MASK;
258         nrf_power_event_clear(NRF_POWER, NRF_POWER_EVENT_SLEEPENTER);
259     }
260     if (p_config->en_exit)
261     {
262         enmask |= NRF_POWER_INT_SLEEPEXIT_MASK;
263         nrf_power_event_clear(NRF_POWER, NRF_POWER_EVENT_SLEEPEXIT);
264     }
265     nrf_power_int_enable(NRF_POWER, enmask);
266 }
267 
nrfx_power_sleepevt_disable(void)268 void nrfx_power_sleepevt_disable(void)
269 {
270     nrf_power_int_disable(NRF_POWER, NRF_POWER_INT_SLEEPENTER_MASK |
271                                      NRF_POWER_INT_SLEEPEXIT_MASK);
272 }
273 
nrfx_power_sleepevt_uninit(void)274 void nrfx_power_sleepevt_uninit(void)
275 {
276     m_sleepevt_handler = NULL;
277 }
278 #endif /* NRF_POWER_HAS_SLEEPEVT */
279 
280 #if (NRF_POWER_HAS_CONST_LATENCY && NRF_POWER_HAS_LOW_POWER)
nrfx_power_constlat_mode_request(void)281 nrfx_err_t nrfx_power_constlat_mode_request(void)
282 {
283     NRFX_ASSERT(m_power_mode_refs != UINT8_MAX);
284 
285     nrfx_err_t err_code = NRFX_ERROR_ALREADY;
286     NRFX_CRITICAL_SECTION_ENTER();
287 
288     m_power_mode_refs++;
289 
290     if (m_power_mode_refs == 1)
291     {
292         nrf_power_task_trigger(NRF_POWER, NRF_POWER_TASK_CONSTLAT);
293         err_code = NRFX_SUCCESS;
294     }
295     NRFX_CRITICAL_SECTION_EXIT();
296 
297     return err_code;
298 }
299 
nrfx_power_constlat_mode_free(void)300 nrfx_err_t nrfx_power_constlat_mode_free(void)
301 {
302     NRFX_ASSERT(m_power_mode_refs != 0);
303 
304     nrfx_err_t err_code = NRFX_ERROR_BUSY;
305     NRFX_CRITICAL_SECTION_ENTER();
306 
307     m_power_mode_refs--;
308 
309     if (m_power_mode_refs == 0)
310     {
311         nrf_power_task_trigger(NRF_POWER, NRF_POWER_TASK_LOWPWR);
312         err_code = NRFX_SUCCESS;
313     }
314     NRFX_CRITICAL_SECTION_EXIT();
315 
316     return err_code;
317 }
318 
nrfx_power_mode_get(void)319 nrfx_power_mode_t nrfx_power_mode_get(void)
320 {
321     return m_power_mode_refs > 0 ? NRFX_POWER_MODE_CONSTLAT : NRFX_POWER_MODE_LOWPWR;
322 }
323 #endif
324 
325 #if NRF_POWER_HAS_USBREG
nrfx_power_usbevt_init(nrfx_power_usbevt_config_t const * p_config)326 void nrfx_power_usbevt_init(nrfx_power_usbevt_config_t const * p_config)
327 {
328     NRFX_ASSERT(p_config != NULL);
329 
330     nrfx_power_usbevt_uninit();
331     if (p_config->handler != NULL)
332     {
333         m_usbevt_handler = p_config->handler;
334     }
335 }
336 
nrfx_power_usbevt_enable(void)337 void nrfx_power_usbevt_enable(void)
338 {
339     nrf_power_int_enable(NRF_POWER, NRF_POWER_INT_USBDETECTED_MASK |
340                                     NRF_POWER_INT_USBREMOVED_MASK  |
341                                     NRF_POWER_INT_USBPWRRDY_MASK);
342 }
343 
nrfx_power_usbevt_disable(void)344 void nrfx_power_usbevt_disable(void)
345 {
346     nrf_power_int_disable(NRF_POWER, NRF_POWER_INT_USBDETECTED_MASK |
347                                      NRF_POWER_INT_USBREMOVED_MASK  |
348                                      NRF_POWER_INT_USBPWRRDY_MASK);
349 }
350 
nrfx_power_usbevt_uninit(void)351 void nrfx_power_usbevt_uninit(void)
352 {
353     nrfx_power_usbevt_disable();
354     m_usbevt_handler = NULL;
355 }
356 
357 
358 #endif /* NRF_POWER_HAS_USBREG */
359 
360 
nrfx_power_irq_handler(void)361 void nrfx_power_irq_handler(void)
362 {
363     uint32_t enabled = nrf_power_int_enable_get(NRF_POWER);
364     /* Prevent "unused variable" warning when all below blocks are disabled. */
365     (void)enabled;
366 
367 #if NRFX_POWER_SUPPORTS_POFCON
368     if ((0 != (enabled & NRF_POWER_INT_POFWARN_MASK)) &&
369         nrf_power_event_get_and_clear(NRF_POWER, NRF_POWER_EVENT_POFWARN))
370     {
371         /* Cannot be null if event is enabled */
372         NRFX_ASSERT(m_pofwarn_handler != NULL);
373         m_pofwarn_handler();
374     }
375 #endif
376 #if NRF_POWER_HAS_SLEEPEVT
377     if ((0 != (enabled & NRF_POWER_INT_SLEEPENTER_MASK)) &&
378         nrf_power_event_get_and_clear(NRF_POWER, NRF_POWER_EVENT_SLEEPENTER))
379     {
380         /* Cannot be null if event is enabled */
381         NRFX_ASSERT(m_sleepevt_handler != NULL);
382         m_sleepevt_handler(NRFX_POWER_SLEEP_EVT_ENTER);
383     }
384     if ((0 != (enabled & NRF_POWER_INT_SLEEPEXIT_MASK)) &&
385         nrf_power_event_get_and_clear(NRF_POWER, NRF_POWER_EVENT_SLEEPEXIT))
386     {
387         /* Cannot be null if event is enabled */
388         NRFX_ASSERT(m_sleepevt_handler != NULL);
389         m_sleepevt_handler(NRFX_POWER_SLEEP_EVT_EXIT);
390     }
391 #endif
392 #if NRF_POWER_HAS_USBREG
393     if ((0 != (enabled & NRF_POWER_INT_USBDETECTED_MASK)) &&
394         nrf_power_event_get_and_clear(NRF_POWER, NRF_POWER_EVENT_USBDETECTED))
395     {
396         /* Cannot be null if event is enabled */
397         NRFX_ASSERT(m_usbevt_handler != NULL);
398         m_usbevt_handler(NRFX_POWER_USB_EVT_DETECTED);
399     }
400     if ((0 != (enabled & NRF_POWER_INT_USBREMOVED_MASK)) &&
401         nrf_power_event_get_and_clear(NRF_POWER, NRF_POWER_EVENT_USBREMOVED))
402     {
403         /* Cannot be null if event is enabled */
404         NRFX_ASSERT(m_usbevt_handler != NULL);
405         m_usbevt_handler(NRFX_POWER_USB_EVT_REMOVED);
406     }
407     if ((0 != (enabled & NRF_POWER_INT_USBPWRRDY_MASK)) &&
408         nrf_power_event_get_and_clear(NRF_POWER, NRF_POWER_EVENT_USBPWRRDY))
409     {
410         /* Cannot be null if event is enabled */
411         NRFX_ASSERT(m_usbevt_handler != NULL);
412         m_usbevt_handler(NRFX_POWER_USB_EVT_READY);
413     }
414 #endif
415 }
416 
417 #if NRFX_CHECK(NRFX_CLOCK_ENABLED)
418 /*
419  * If both POWER and CLOCK drivers are used, a common IRQ handler function must
420  * be used that calls the handlers in these two drivers. This is because these
421  * two peripherals share one interrupt.
422  * This function is located here, not in a separate nrfx_power_clock.c file,
423  * so that it does not end up as the only symbol in a separate object when
424  * a library with nrfx is created. In such case, forcing a linker to use this
425  * function instead of another one defined as weak will require additional
426  * actions, and might be even impossible.
427  */
nrfx_power_clock_irq_handler(void)428 void nrfx_power_clock_irq_handler(void)
429 {
430     nrfx_power_irq_handler();
431     nrfx_clock_irq_handler();
432 }
433 #endif
434 
435 #endif // NRFX_CHECK(NRFX_POWER_ENABLED)
436