Skip to content

Commit 4d30759

Browse files
committed
[nrf fromlist] soc: nordic: common: nrf_sys_event: Add API for registering an event
Add API for indicating that a deterministic interrupt will occur at certain point in time in the future. Implementation is ensuring that there will be no latency due to NVM memory waking up. There are 2 ways of ensuring that: - setting low latency power mode in RRAMC (higher power consumption in idle (not available in non-secure build) - using PPI and GRTC to trigger the RRAMC wake up task right before an expected interrupt Module has a pool of GRTC channels and dynamically allocates and frees those channels when events are registered and unregistered. If GRTC channel is not available then algorithm falls back to power mode setting (in secure build). API offers registering an event using relative and absolute timing. API can be used from Zero Latency interrupts. Upstream PR #: 99377 Signed-off-by: Krzysztof Chruściński <[email protected]>
1 parent b58e4cc commit 4d30759

File tree

3 files changed

+266
-1
lines changed

3 files changed

+266
-1
lines changed

soc/nordic/common/Kconfig

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,44 @@ config NRF_SYS_EVENT
2121
bool "nRF system event support"
2222
select NRFX_POWER if !NRF_PLATFORM_HALTIUM
2323

24+
if NRF_SYS_EVENT
25+
26+
config NRF_SYS_EVENT_IRQ_LATENCY
27+
bool "Register events to reduce interrupt handling latency"
28+
default y
29+
depends on HAS_HW_NRF_RRAMC && !TRUSTED_EXECUTION_NONSECURE
30+
31+
config NRF_SYS_EVENT_IRQ_LATENCY_MANUAL
32+
def_bool !RISCV
33+
depends on NRF_SYS_EVENT_IRQ_LATENCY
34+
help
35+
If enabled, that indicates that manual control of NVM memory power
36+
mode is available.
37+
38+
config NRF_SYS_EVENT_GRTC_CHAN_CNT
39+
int "GRTC channel for RRAMC wake up"
40+
default 1
41+
range 0 16
42+
depends on NRF_SYS_EVENT_IRQ_LATENCY
43+
help
44+
Maximum number of GRTC channel dedicated for waking up RRAMC using DPPI
45+
connection between GRTC compare channel and RRAMC wake up task.
46+
47+
config NRF_SYS_EVENT_USE_GPPI
48+
def_bool NRF_SYS_EVENT_GRTC_CHAN_CNT > 0
49+
select NRFX_GPPI
50+
2451
config SOC_NRF_FORCE_CONSTLAT
2552
bool "Force constant latency mode in system ON"
26-
depends on NRF_SYS_EVENT && !RISCV
53+
depends on !RISCV
2754
help
2855
In constant latency mode the CPU wakeup latency and the PPI task response
2956
will be constant and kept at a minimum. This is secured by forcing a set
3057
of base resources on while in sleep. The advantage of having a constant
3158
and predictable latency will be at the cost of having increased power consumption.
3259

60+
endif # NRF_SYS_EVENT
61+
3362
config MRAM_LATENCY
3463
bool "MRAM latency manager"
3564
depends on NRFS_HAS_MRAM_SERVICE

soc/nordic/common/nrf_sys_event.c

Lines changed: 181 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,16 @@
55
*/
66

77
#include <nrf_sys_event.h>
8+
#include <helpers/nrfx_gppi.h>
9+
#include <zephyr/logging/log.h>
10+
#ifdef CONFIG_NRF_SYS_EVENT_IRQ_LATENCY
11+
#include <nrfx_grtc.h>
12+
#include <zephyr/drivers/timer/nrf_grtc_timer.h>
13+
#ifdef RRAMC_PRESENT
14+
#include <hal/nrf_rramc.h>
15+
#endif
16+
#endif
17+
LOG_MODULE_DECLARE(soc, CONFIG_SOC_LOG_LEVEL);
818

919
#if CONFIG_SOC_SERIES_NRF54HX
1020

@@ -93,3 +103,174 @@ int nrf_sys_event_release_global_constlat(void)
93103
}
94104

95105
#endif
106+
107+
#ifdef CONFIG_NRF_SYS_EVENT_IRQ_LATENCY
108+
BUILD_ASSERT(IS_ENABLED(CONFIG_NRF_SYS_EVENT_IRQ_LATENCY_MANUAL) ||
109+
(CONFIG_NRF_SYS_EVENT_GRTC_CHAN_CNT > 0),
110+
"If manual mode is not available then at least 1 GRTC channel need to be used.");
111+
112+
static uint32_t event_ref_cnt;
113+
static uint32_t chan_mask;
114+
115+
#define NVM_HW_WAKEUP_US 16
116+
#define NVM_MANUAL_SUPPORT IS_ENABLED(CONFIG_NRF_SYS_EVENT_IRQ_LATENCY_MANUAL)
117+
/* Due to software performance and risk of waking up too early (then RRAMC may go
118+
* to sleep before interrupt), it's better to adjust a bit.
119+
*/
120+
#define NVM_WAKEUP_US (NVM_HW_WAKEUP_US - 1)
121+
122+
static void irq_low_latency_on(bool enable)
123+
{
124+
#ifdef RRAMC_POWER_LOWPOWERCONFIG_MODE_Standby
125+
nrf_rramc_lp_mode_set(NRF_RRAMC, enable ? NRF_RRAMC_LP_STANDBY : NRF_RRAMC_LP_POWER_OFF);
126+
#endif
127+
}
128+
129+
#ifdef CONFIG_ZERO_LATENCY_IRQS
130+
static uint32_t full_irq_lock(void)
131+
{
132+
uint32_t mcu_critical_state;
133+
134+
mcu_critical_state = __get_PRIMASK();
135+
__disable_irq();
136+
137+
return mcu_critical_state;
138+
}
139+
140+
static void full_irq_unlock(uint32_t mcu_critical_state)
141+
{
142+
__set_PRIMASK(mcu_critical_state);
143+
}
144+
145+
#define LOCKED(lock) \
146+
for (uint32_t __tmp = 0, __key = full_irq_lock(); !__tmp; full_irq_unlock(__key), __tmp = 1)
147+
#else
148+
static struct k_spinlock event_lock;
149+
#define LOCKED() K_SPINLOCK(&event_lock)
150+
#endif
151+
152+
union nrf_sys_evt_us {
153+
uint32_t rel;
154+
uint64_t abs;
155+
};
156+
157+
int event_register(union nrf_sys_evt_us us, bool force, bool abs)
158+
{
159+
int rv;
160+
161+
LOCKED() {
162+
if ((CONFIG_NRF_SYS_EVENT_GRTC_CHAN_CNT > 0) &&
163+
((abs == false) && ((us.rel >= NVM_WAKEUP_US) || !NVM_MANUAL_SUPPORT)) &&
164+
(chan_mask != 0)) {
165+
rv = __builtin_ctz(chan_mask);
166+
chan_mask &= ~BIT(rv);
167+
if (abs) {
168+
nrfy_grtc_sys_counter_cc_set(NRF_GRTC, rv,
169+
us.abs - NVM_WAKEUP_US);
170+
} else {
171+
uint32_t val = (NVM_MANUAL_SUPPORT || (us.rel >= NVM_WAKEUP_US)) ?
172+
(us.rel - NVM_WAKEUP_US) : 1;
173+
174+
nrfx_grtc_syscounter_cc_rel_set(rv, val,
175+
NRFX_GRTC_CC_RELATIVE_SYSCOUNTER);
176+
}
177+
} else if ((chan_mask == 0) && (force == false)) {
178+
rv = -ENOSYS;
179+
} else {
180+
if (event_ref_cnt == 0) {
181+
irq_low_latency_on(true);
182+
}
183+
event_ref_cnt++;
184+
rv = 32;
185+
}
186+
}
187+
188+
return rv;
189+
}
190+
191+
int nrf_sys_event_register(uint32_t us, bool force)
192+
{
193+
return event_register((union nrf_sys_evt_us)us, force, false);
194+
}
195+
196+
int nrf_sys_event_abs_register(uint64_t us, bool force)
197+
{
198+
return event_register((union nrf_sys_evt_us)us, force, true);
199+
}
200+
201+
int nrf_sys_event_unregister(int handle, bool cancel)
202+
{
203+
__ASSERT_NO_MSG(handle >= 0);
204+
int rv = 0;
205+
206+
if (handle < 32) {
207+
if (cancel) {
208+
nrf_grtc_sys_counter_compare_event_disable(NRF_GRTC, handle);
209+
}
210+
atomic_or((atomic_t *)&chan_mask, BIT(handle));
211+
}
212+
213+
LOCKED() {
214+
if (IS_ENABLED(CONFIG_TRUSTED_EXECUTION_NONSECURE)) {
215+
rv = -EINVAL;
216+
} else {
217+
__ASSERT_NO_MSG(event_ref_cnt > 0);
218+
event_ref_cnt--;
219+
if (event_ref_cnt == 0) {
220+
irq_low_latency_on(false);
221+
}
222+
}
223+
}
224+
225+
return rv;
226+
}
227+
228+
#if CONFIG_NRF_SYS_EVENT_GRTC_CHAN_CNT > 0
229+
int nrf_sys_event_init(void)
230+
{
231+
/* Attempt to allocate requested amount of GRTC channels. */
232+
for (int i = 0; i < CONFIG_NRF_SYS_EVENT_GRTC_CHAN_CNT; i++) {
233+
int ch = z_nrf_grtc_timer_chan_alloc();
234+
235+
if (ch < 0) {
236+
LOG_WRN("Allocated less GRTC channels (%d) than requested (%d)",
237+
i, CONFIG_NRF_SYS_EVENT_GRTC_CHAN_CNT);
238+
break;
239+
}
240+
chan_mask |= BIT(ch);
241+
}
242+
243+
uint32_t chan_mask_cpy = chan_mask;
244+
uint32_t tsk = nrf_rramc_task_address_get(NRF_RRAMC, NRF_RRAMC_TASK_WAKEUP);
245+
bool first = true;
246+
nrfx_gppi_handle_t ppi_handle;
247+
nrf_grtc_event_t cc_evt;
248+
uint32_t evt;
249+
uint32_t ch;
250+
int err;
251+
252+
/* Setup a PPI connection between allocated GRTC channels and RRAMC wake up task. */
253+
while (chan_mask_cpy) {
254+
ch = __builtin_ctz(chan_mask_cpy);
255+
chan_mask_cpy &= ~BIT(ch);
256+
cc_evt = nrf_grtc_sys_counter_compare_event_get(ch);
257+
evt = nrf_grtc_event_address_get(NRF_GRTC, cc_evt);
258+
if (first) {
259+
first = false;
260+
err = nrfx_gppi_conn_alloc(evt, tsk, &ppi_handle);
261+
if (err < 0) {
262+
return err;
263+
}
264+
} else {
265+
nrfx_gppi_ep_attach(evt, ppi_handle);
266+
}
267+
}
268+
269+
nrfx_gppi_conn_enable(ppi_handle);
270+
271+
return 0;
272+
}
273+
274+
SYS_INIT(nrf_sys_event_init, PRE_KERNEL_1, 0);
275+
#endif /* CONFIG_NRF_SYS_EVENT_GRTC_CHAN_CNT > 0 */
276+
#endif /* CONFIG_NRF_SYS_EVENT_IRQ_LATENCY */

soc/nordic/common/nrf_sys_event.h

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,58 @@ int nrf_sys_event_request_global_constlat(void);
2828
* @retval -errno code otherwise
2929
*/
3030
int nrf_sys_event_release_global_constlat(void);
31+
32+
/**
33+
* @brief Register an event (interrupt) using relative time.
34+
*
35+
* Registering an event allows system to prepare for wake up minimizing power consumption
36+
* and latency. There are 2 ways of minimizing interrupt latency:
37+
* - Setting low latency power mode for NVM memory. This option is not available in non-secure
38+
* build.
39+
* - Using DPPI to wake up memory before the expected interrupt (if enabled). This option requires
40+
* additional resource (DPPI and GRTC) which are allocated during registration and freed in
41+
* nrf_sys_event_unregister. If resources are not available then algorithm falls back
42+
* to the option 1. Timing should be precise as if memory is woken up it will go back to
43+
* sleep if it is not used for certain amount of time (in case of RRAMC 4 us by default).
44+
* CONFIG_NRF_SYS_EVENT_GRTC_CHAN_CNT configures target amount of GRTC channels. During
45+
* the initialization those channels are dynamically allocated so it is possible that less
46+
* are available.
47+
*
48+
* @param us Time (in microseconds) from now when interrupt is expected to be triggered.
49+
*
50+
* @param force If true then low latency mode if forced when there is no resources available
51+
* for NVM memory wake up.
52+
*
53+
* @retval non-negative Handle which shall be used to unregister the event.
54+
* @retval -ENOTSUP Feature is not supported.
55+
*/
56+
int nrf_sys_event_register(uint32_t us, bool force);
57+
58+
/**
59+
* @brief Register an event (interrupt) using absolute time.
60+
*
61+
* See nrf_sys_event_register for more details.
62+
*
63+
* @param us Absolute time (in microseconds) when interrupt is expected to be triggered.
64+
*
65+
* @param force If true then low latency mode if forced when there is no resources available
66+
* for NVM memory wake up.
67+
*
68+
* @retval non-negative Handle which shall be used to unregister the event.
69+
* @retval -ENOTSUP Feature is not supported.
70+
*/
71+
int nrf_sys_event_abs_register(uint64_t us, bool force);
72+
73+
/** @brief Unregister an event.
74+
*
75+
* It must be called after the registered event occurred or if it was canceled.
76+
*
77+
* @param handle Handle returned by @ref nrf_sys_event_register.
78+
* @param cancel True to indicate that event is unregistered due to cancellation. Setting to true
79+
* when event executed results only in slight longer duration of the operation.
80+
*
81+
* @retval 0 Successful operation.
82+
* @retval -EINVAL Invalid handle.
83+
* @retval -ENOTSUP Not supported.
84+
*/
85+
int nrf_sys_event_unregister(int handle, bool cancel);

0 commit comments

Comments
 (0)