Skip to content

Commit c27a8e1

Browse files
manuarguecarlescufi
authored andcommitted
drivers: counter: nxp_sys_timer: support late and short alarms
Support short relative and late alarms for NXP System Timer Module counter driver. The late alarm detection algorithm applied, is based on existing counter drivers. Signed-off-by: Manuel Argüelles <[email protected]>
1 parent 3cccad5 commit c27a8e1

File tree

1 file changed

+132
-16
lines changed

1 file changed

+132
-16
lines changed

drivers/counter/counter_nxp_s32_sys_timer.c

Lines changed: 132 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,9 @@
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

5558
struct 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

5964
struct 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+
68107
static 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+
75176
static 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

165256
static 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+
209322
static 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

Comments
 (0)