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