99#include <zephyr/kernel.h>
1010#include <zephyr/drivers/counter.h>
1111#include <zephyr/drivers/clock_control.h>
12+ #if defined(CONFIG_GIC )
13+ #include <zephyr/drivers/interrupt_controller/gic.h>
14+ #endif /* CONFIG_GIC */
1215#include <zephyr/logging/log.h>
1316#include <zephyr/irq.h>
1417
@@ -54,6 +57,8 @@ struct nxp_s32_sys_timer_chan_data {
5457
5558struct nxp_s32_sys_timer_data {
5659 struct nxp_s32_sys_timer_chan_data ch_data [SYS_TIMER_NUM_CHANNELS ];
60+ uint32_t guard_period ;
61+ atomic_t irq_pending ;
5762};
5863
5964struct nxp_s32_sys_timer_config {
@@ -63,15 +68,111 @@ struct nxp_s32_sys_timer_config {
6368 clock_control_subsys_t clock_subsys ;
6469 uint8_t prescaler ;
6570 bool freeze ;
71+ unsigned int irqn ;
6672};
6773
74+ static ALWAYS_INLINE void irq_set_pending (unsigned int irq )
75+ {
76+ #if defined(CONFIG_GIC )
77+ arm_gic_irq_set_pending (irq );
78+ #else
79+ NVIC_SetPendingIRQ (irq );
80+ #endif /* CONFIG_GIC */
81+ }
82+
83+ static uint32_t ticks_add (uint32_t val1 , uint32_t val2 , uint32_t top )
84+ {
85+ uint32_t to_top ;
86+
87+ if (likely (IS_BIT_MASK (top ))) {
88+ return (val1 + val2 ) & top ;
89+ }
90+
91+ /* top is not 2^n-1 */
92+ to_top = top - val1 ;
93+
94+ return (val2 <= to_top ) ? val1 + val2 : val2 - to_top ;
95+ }
96+
97+ static uint32_t ticks_sub (uint32_t val , uint32_t old , uint32_t top )
98+ {
99+ if (likely (IS_BIT_MASK (top ))) {
100+ return (val - old ) & top ;
101+ }
102+
103+ /* top is not 2^n-1 */
104+ return (val >= old ) ? (val - old ) : val + top + 1 - old ;
105+ }
106+
68107static ALWAYS_INLINE void stm_disable_channel (const struct nxp_s32_sys_timer_config * config ,
69108 uint8_t channel )
70109{
71110 REG_WRITE (STM_CCR (channel ), STM_CCR_CEN (0U ));
72111 REG_WRITE (STM_CIR (channel ), STM_CIR_CIF (1U ));
73112}
74113
114+ static int stm_set_alarm (const struct device * dev , uint8_t channel , uint32_t ticks , uint32_t flags )
115+ {
116+ const struct nxp_s32_sys_timer_config * config = dev -> config ;
117+ struct nxp_s32_sys_timer_data * data = dev -> data ;
118+ struct nxp_s32_sys_timer_chan_data * ch_data = & data -> ch_data [channel ];
119+ const uint32_t now = REG_READ (STM_CNT );
120+ const uint32_t top_val = config -> info .max_top_value ;
121+ int err = 0 ;
122+ uint32_t diff ;
123+ uint32_t max_rel_val ;
124+ bool irq_on_late ;
125+
126+ if (flags & COUNTER_ALARM_CFG_ABSOLUTE ) {
127+ __ASSERT_NO_MSG (data -> guard_period < top_val );
128+ max_rel_val = top_val - data -> guard_period ;
129+ irq_on_late = !!(flags & COUNTER_ALARM_CFG_EXPIRE_WHEN_LATE );
130+ } else {
131+ /*
132+ * If relative value is smaller than half of the counter range it is assumed
133+ * that there is a risk of setting value too late and late detection algorithm
134+ * must be applied. When late setting is detected, interrupt shall be
135+ * triggered for immediate expiration of the timer. Detection is performed
136+ * by limiting relative distance between CMP and CNT.
137+ *
138+ * Note that half of counter range is an arbitrary value.
139+ */
140+ irq_on_late = ticks < (top_val / 2 );
141+ /* Limit max to detect short relative being set too late */
142+ max_rel_val = irq_on_late ? top_val / 2 : top_val ;
143+ ticks = ticks_add (now , ticks , top_val );
144+ }
145+
146+ /* Disable the channel before loading the new value so that it takes effect immediately */
147+ stm_disable_channel (config , channel );
148+ REG_WRITE (STM_CMP (channel ), ticks );
149+ REG_WRITE (STM_CCR (channel ), STM_CCR_CEN (1U ));
150+
151+ /*
152+ * Decrement value to detect also case when ticks == CNT. Otherwise, condition would need
153+ * to include comparing diff against 0.
154+ */
155+ diff = ticks_sub (ticks - 1 , REG_READ (STM_CNT ), top_val );
156+ if (diff > max_rel_val ) {
157+ if (flags & COUNTER_ALARM_CFG_ABSOLUTE ) {
158+ err = - ETIME ;
159+ }
160+
161+ /*
162+ * Interrupt is triggered always for relative alarm and for absolute depending
163+ * on the flag
164+ */
165+ if (irq_on_late ) {
166+ atomic_or (& data -> irq_pending , BIT (channel ));
167+ irq_set_pending (config -> irqn );
168+ } else {
169+ ch_data -> callback = NULL ;
170+ }
171+ }
172+
173+ return err ;
174+ }
175+
75176static void stm_isr (const struct device * dev )
76177{
77178 const struct nxp_s32_sys_timer_config * config = dev -> config ;
@@ -86,8 +187,9 @@ static void stm_isr(const struct device *dev)
86187 pending = FIELD_GET (STM_CCR_CEN_MASK , REG_READ (STM_CCR (channel ))) &&
87188 FIELD_GET (STM_CIR_CIF_MASK , REG_READ (STM_CIR (channel )));
88189
89- if (pending ) {
190+ if (pending || atomic_test_bit ( & data -> irq_pending , channel ) ) {
90191 stm_disable_channel (config , channel );
192+ atomic_and (& data -> irq_pending , ~BIT (channel ));
91193
92194 ch_data = & data -> ch_data [channel ];
93195 if (ch_data -> callback ) {
@@ -135,31 +237,20 @@ static int nxp_s32_sys_timer_set_alarm(const struct device *dev, uint8_t channel
135237 const struct nxp_s32_sys_timer_config * config = dev -> config ;
136238 struct nxp_s32_sys_timer_data * data = dev -> data ;
137239 struct nxp_s32_sys_timer_chan_data * ch_data = & data -> ch_data [channel ];
138- uint32_t ticks = alarm_cfg -> ticks ;
139- uint32_t cnt_val = REG_READ (STM_CNT );
140240
141241 if (ch_data -> callback ) {
142242 return - EBUSY ;
143243 }
144244
145- if (ticks > config -> info .max_top_value ) {
146- LOG_ERR ("Invalid ticks value %d" , ticks );
245+ if (alarm_cfg -> ticks > config -> info .max_top_value ) {
246+ LOG_ERR ("Invalid ticks value %d" , alarm_cfg -> ticks );
147247 return - EINVAL ;
148248 }
149249
150250 ch_data -> callback = alarm_cfg -> callback ;
151251 ch_data -> user_data = alarm_cfg -> user_data ;
152252
153- /* Disable the channel before loading the new value so that it takes effect immediately */
154- stm_disable_channel (config , channel );
155-
156- if (!(alarm_cfg -> flags & COUNTER_ALARM_CFG_ABSOLUTE )) {
157- ticks += cnt_val ;
158- }
159- REG_WRITE (STM_CMP (channel ), ticks );
160- REG_WRITE (STM_CCR (channel ), STM_CCR_CEN (1U ));
161-
162- return 0 ;
253+ return stm_set_alarm (dev , channel , alarm_cfg -> ticks , alarm_cfg -> flags );
163254}
164255
165256static int nxp_s32_sys_timer_cancel_alarm (const struct device * dev , uint8_t channel )
@@ -206,6 +297,28 @@ static uint32_t nxp_s32_sys_timer_get_top_value(const struct device *dev)
206297 return config -> info .max_top_value ;
207298}
208299
300+ static int nxp_s32_sys_timer_set_guard_period (const struct device * dev , uint32_t guard ,
301+ uint32_t flags )
302+ {
303+ struct nxp_s32_sys_timer_data * data = dev -> data ;
304+
305+ ARG_UNUSED (flags );
306+
307+ __ASSERT_NO_MSG (guard < nxp_s32_sys_timer_get_top_value (dev ));
308+ data -> guard_period = guard ;
309+
310+ return 0 ;
311+ }
312+
313+ static uint32_t nxp_s32_sys_timer_get_guard_period (const struct device * dev , uint32_t flags )
314+ {
315+ struct nxp_s32_sys_timer_data * data = dev -> data ;
316+
317+ ARG_UNUSED (flags );
318+
319+ return data -> guard_period ;
320+ }
321+
209322static uint32_t nxp_s32_sys_timer_get_frequency (const struct device * dev )
210323{
211324 const struct nxp_s32_sys_timer_config * config = dev -> config ;
@@ -264,8 +377,10 @@ static const struct counter_driver_api nxp_s32_sys_timer_driver_api = {
264377 .set_alarm = nxp_s32_sys_timer_set_alarm ,
265378 .cancel_alarm = nxp_s32_sys_timer_cancel_alarm ,
266379 .set_top_value = nxp_s32_sys_timer_set_top_value ,
267- .get_pending_int = nxp_s32_sys_timer_get_pending_int ,
268380 .get_top_value = nxp_s32_sys_timer_get_top_value ,
381+ .set_guard_period = nxp_s32_sys_timer_set_guard_period ,
382+ .get_guard_period = nxp_s32_sys_timer_get_guard_period ,
383+ .get_pending_int = nxp_s32_sys_timer_get_pending_int ,
269384 .get_freq = nxp_s32_sys_timer_get_frequency
270385};
271386
@@ -294,6 +409,7 @@ static const struct counter_driver_api nxp_s32_sys_timer_driver_api = {
294409 .prescaler = DT_INST_PROP(n, prescaler) - 1, \
295410 .clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)), \
296411 .clock_subsys = (clock_control_subsys_t)DT_INST_CLOCKS_CELL(n, name), \
412+ .irqn = DT_INST_IRQN(n), \
297413 }; \
298414 \
299415 DEVICE_DT_INST_DEFINE(n, \
0 commit comments