1 /*
2 * Copyright (c) 2017 - 2024, 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 NRFX_IRQ_DISABLE(nrfx_get_irq_number(NRF_POWER));
157 }
158 #if NRFX_POWER_SUPPORTS_POFCON
159 nrfx_power_pof_uninit();
160 #endif
161 #if NRF_POWER_HAS_SLEEPEVT
162 nrfx_power_sleepevt_uninit();
163 #endif
164 #if NRF_POWER_HAS_USBREG || defined(USBREG_PRESENT)
165 nrfx_power_usbevt_uninit();
166 #endif
167 m_initialized = false;
168 }
169
nrfx_power_init_check(void)170 bool nrfx_power_init_check(void)
171 {
172 return m_initialized;
173 }
174
175 #if NRFX_POWER_SUPPORTS_POFCON
nrfx_power_pof_init(nrfx_power_pofwarn_config_t const * p_config)176 void nrfx_power_pof_init(nrfx_power_pofwarn_config_t const * p_config)
177 {
178 NRFX_ASSERT(p_config != NULL);
179
180 nrfx_power_pof_uninit();
181
182 if (p_config->handler != NULL)
183 {
184 m_pofwarn_handler = p_config->handler;
185 }
186 }
187
nrfx_power_pof_enable(nrfx_power_pofwarn_config_t const * p_config)188 void nrfx_power_pof_enable(nrfx_power_pofwarn_config_t const * p_config)
189 {
190 #if NRF_POWER_HAS_POFCON
191 nrf_power_pofcon_set(NRF_POWER, true, p_config->thr);
192 #if NRF_POWER_HAS_POFCON_VDDH
193 nrf_power_pofcon_vddh_set(NRF_POWER, p_config->thrvddh);
194 #endif
195 #elif NRF_REGULATORS_HAS_POF
196 nrf_regulators_pof_config_t pof_config = {
197 .enable = true,
198 .thr = p_config->thr,
199 #if NRF_REGULATORS_HAS_POF_VDDH
200 .thr_vddh = p_config->thrvddh,
201 #endif
202 #if NRF_REGULATORS_HAS_POF_WARN_DISABLE
203 .warn_disable = false,
204 #endif
205 };
206 nrf_regulators_pof_config_set(NRF_REGULATORS, &pof_config);
207 #endif
208
209 if (m_pofwarn_handler != NULL)
210 {
211 nrf_power_int_enable(NRF_POWER, NRF_POWER_INT_POFWARN_MASK);
212 }
213 }
214
nrfx_power_pof_disable(void)215 void nrfx_power_pof_disable(void)
216 {
217 #if NRF_POWER_HAS_POFCON
218 nrf_power_pofcon_set(NRF_POWER, false, NRF_POWER_POFTHR_V27);
219 #elif NRF_REGULATORS_HAS_POF
220 nrf_regulators_pof_config_t pof_config = {
221 .enable = false,
222 .thr = NRF_REGULATORS_POF_THR_2V7,
223 };
224 nrf_regulators_pof_config_set(NRF_REGULATORS, &pof_config);
225 #endif
226 nrf_power_int_disable(NRF_POWER, NRF_POWER_INT_POFWARN_MASK);
227 }
228
nrfx_power_pof_uninit(void)229 void nrfx_power_pof_uninit(void)
230 {
231 m_pofwarn_handler = NULL;
232 }
233 #endif // NRFX_POWER_SUPPORTS_POFCON
234
235 #if NRF_POWER_HAS_SLEEPEVT
nrfx_power_sleepevt_init(nrfx_power_sleepevt_config_t const * p_config)236 void nrfx_power_sleepevt_init(nrfx_power_sleepevt_config_t const * p_config)
237 {
238 NRFX_ASSERT(p_config != NULL);
239
240 nrfx_power_sleepevt_uninit();
241 if (p_config->handler != NULL)
242 {
243 m_sleepevt_handler = p_config->handler;
244 }
245 }
246
nrfx_power_sleepevt_enable(nrfx_power_sleepevt_config_t const * p_config)247 void nrfx_power_sleepevt_enable(nrfx_power_sleepevt_config_t const * p_config)
248 {
249 uint32_t enmask = 0;
250 if (p_config->en_enter)
251 {
252 enmask |= NRF_POWER_INT_SLEEPENTER_MASK;
253 nrf_power_event_clear(NRF_POWER, NRF_POWER_EVENT_SLEEPENTER);
254 }
255 if (p_config->en_exit)
256 {
257 enmask |= NRF_POWER_INT_SLEEPEXIT_MASK;
258 nrf_power_event_clear(NRF_POWER, NRF_POWER_EVENT_SLEEPEXIT);
259 }
260 nrf_power_int_enable(NRF_POWER, enmask);
261 }
262
nrfx_power_sleepevt_disable(void)263 void nrfx_power_sleepevt_disable(void)
264 {
265 nrf_power_int_disable(NRF_POWER, NRF_POWER_INT_SLEEPENTER_MASK |
266 NRF_POWER_INT_SLEEPEXIT_MASK);
267 }
268
nrfx_power_sleepevt_uninit(void)269 void nrfx_power_sleepevt_uninit(void)
270 {
271 m_sleepevt_handler = NULL;
272 }
273 #endif /* NRF_POWER_HAS_SLEEPEVT */
274
275 #if (NRF_POWER_HAS_CONST_LATENCY && NRF_POWER_HAS_LOW_POWER)
nrfx_power_constlat_mode_request(void)276 nrfx_err_t nrfx_power_constlat_mode_request(void)
277 {
278 NRFX_ASSERT(m_power_mode_refs != UINT8_MAX);
279
280 nrfx_err_t err_code = NRFX_ERROR_ALREADY;
281 NRFX_CRITICAL_SECTION_ENTER();
282
283 m_power_mode_refs++;
284
285 if (m_power_mode_refs == 1)
286 {
287 nrf_power_task_trigger(NRF_POWER, NRF_POWER_TASK_CONSTLAT);
288 err_code = NRFX_SUCCESS;
289 }
290 NRFX_CRITICAL_SECTION_EXIT();
291
292 return err_code;
293 }
294
nrfx_power_constlat_mode_free(void)295 nrfx_err_t nrfx_power_constlat_mode_free(void)
296 {
297 NRFX_ASSERT(m_power_mode_refs != 0);
298
299 nrfx_err_t err_code = NRFX_ERROR_BUSY;
300 NRFX_CRITICAL_SECTION_ENTER();
301
302 m_power_mode_refs--;
303
304 if (m_power_mode_refs == 0)
305 {
306 nrf_power_task_trigger(NRF_POWER, NRF_POWER_TASK_LOWPWR);
307 err_code = NRFX_SUCCESS;
308 }
309 NRFX_CRITICAL_SECTION_EXIT();
310
311 return err_code;
312 }
313
nrfx_power_mode_get(void)314 nrfx_power_mode_t nrfx_power_mode_get(void)
315 {
316 return m_power_mode_refs > 0 ? NRFX_POWER_MODE_CONSTLAT : NRFX_POWER_MODE_LOWPWR;
317 }
318 #endif
319
320 #if NRF_POWER_HAS_USBREG
nrfx_power_usbevt_init(nrfx_power_usbevt_config_t const * p_config)321 void nrfx_power_usbevt_init(nrfx_power_usbevt_config_t const * p_config)
322 {
323 NRFX_ASSERT(p_config != NULL);
324
325 nrfx_power_usbevt_uninit();
326 if (p_config->handler != NULL)
327 {
328 m_usbevt_handler = p_config->handler;
329 }
330 }
331
nrfx_power_usbevt_enable(void)332 void nrfx_power_usbevt_enable(void)
333 {
334 nrf_power_int_enable(NRF_POWER, NRF_POWER_INT_USBDETECTED_MASK |
335 NRF_POWER_INT_USBREMOVED_MASK |
336 NRF_POWER_INT_USBPWRRDY_MASK);
337 }
338
nrfx_power_usbevt_disable(void)339 void nrfx_power_usbevt_disable(void)
340 {
341 nrf_power_int_disable(NRF_POWER, NRF_POWER_INT_USBDETECTED_MASK |
342 NRF_POWER_INT_USBREMOVED_MASK |
343 NRF_POWER_INT_USBPWRRDY_MASK);
344 }
345
nrfx_power_usbevt_uninit(void)346 void nrfx_power_usbevt_uninit(void)
347 {
348 nrfx_power_usbevt_disable();
349 m_usbevt_handler = NULL;
350 }
351
352
353 #endif /* NRF_POWER_HAS_USBREG */
354
355
nrfx_power_irq_handler(void)356 void nrfx_power_irq_handler(void)
357 {
358 uint32_t enabled = nrf_power_int_enable_get(NRF_POWER);
359 /* Prevent "unused variable" warning when all below blocks are disabled. */
360 (void)enabled;
361
362 #if NRFX_POWER_SUPPORTS_POFCON
363 if ((0 != (enabled & NRF_POWER_INT_POFWARN_MASK)) &&
364 nrf_power_event_get_and_clear(NRF_POWER, NRF_POWER_EVENT_POFWARN))
365 {
366 /* Cannot be null if event is enabled */
367 NRFX_ASSERT(m_pofwarn_handler != NULL);
368 m_pofwarn_handler();
369 }
370 #endif
371 #if NRF_POWER_HAS_SLEEPEVT
372 if ((0 != (enabled & NRF_POWER_INT_SLEEPENTER_MASK)) &&
373 nrf_power_event_get_and_clear(NRF_POWER, NRF_POWER_EVENT_SLEEPENTER))
374 {
375 /* Cannot be null if event is enabled */
376 NRFX_ASSERT(m_sleepevt_handler != NULL);
377 m_sleepevt_handler(NRFX_POWER_SLEEP_EVT_ENTER);
378 }
379 if ((0 != (enabled & NRF_POWER_INT_SLEEPEXIT_MASK)) &&
380 nrf_power_event_get_and_clear(NRF_POWER, NRF_POWER_EVENT_SLEEPEXIT))
381 {
382 /* Cannot be null if event is enabled */
383 NRFX_ASSERT(m_sleepevt_handler != NULL);
384 m_sleepevt_handler(NRFX_POWER_SLEEP_EVT_EXIT);
385 }
386 #endif
387 #if NRF_POWER_HAS_USBREG
388 if ((0 != (enabled & NRF_POWER_INT_USBDETECTED_MASK)) &&
389 nrf_power_event_get_and_clear(NRF_POWER, NRF_POWER_EVENT_USBDETECTED))
390 {
391 /* Cannot be null if event is enabled */
392 NRFX_ASSERT(m_usbevt_handler != NULL);
393 m_usbevt_handler(NRFX_POWER_USB_EVT_DETECTED);
394 }
395 if ((0 != (enabled & NRF_POWER_INT_USBREMOVED_MASK)) &&
396 nrf_power_event_get_and_clear(NRF_POWER, NRF_POWER_EVENT_USBREMOVED))
397 {
398 /* Cannot be null if event is enabled */
399 NRFX_ASSERT(m_usbevt_handler != NULL);
400 m_usbevt_handler(NRFX_POWER_USB_EVT_REMOVED);
401 }
402 if ((0 != (enabled & NRF_POWER_INT_USBPWRRDY_MASK)) &&
403 nrf_power_event_get_and_clear(NRF_POWER, NRF_POWER_EVENT_USBPWRRDY))
404 {
405 /* Cannot be null if event is enabled */
406 NRFX_ASSERT(m_usbevt_handler != NULL);
407 m_usbevt_handler(NRFX_POWER_USB_EVT_READY);
408 }
409 #endif
410 }
411
412 #if NRFX_CHECK(NRFX_CLOCK_ENABLED)
413 /*
414 * If both POWER and CLOCK drivers are used, a common IRQ handler function must
415 * be used that calls the handlers in these two drivers. This is because these
416 * two peripherals share one interrupt.
417 * This function is located here, not in a separate nrfx_power_clock.c file,
418 * so that it does not end up as the only symbol in a separate object when
419 * a library with nrfx is created. In such case, forcing a linker to use this
420 * function instead of another one defined as weak will require additional
421 * actions, and might be even impossible.
422 */
nrfx_power_clock_irq_handler(void)423 void nrfx_power_clock_irq_handler(void)
424 {
425 nrfx_power_irq_handler();
426 nrfx_clock_irq_handler();
427 }
428 #endif
429
430 #endif // NRFX_CHECK(NRFX_POWER_ENABLED)
431