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