diff --git a/drivers/counter/counter_mcux_ctimer.c b/drivers/counter/counter_mcux_ctimer.c index 86164436604..3fbc5a6bb21 100644 --- a/drivers/counter/counter_mcux_ctimer.c +++ b/drivers/counter/counter_mcux_ctimer.c @@ -12,7 +12,8 @@ #include LOG_MODULE_REGISTER(mcux_ctimer, CONFIG_COUNTER_LOG_LEVEL); -#define NUM_CHANNELS 4 +/* One of the CTimer channels is reserved to implement set_top_value API */ +#define NUM_CHANNELS 3 struct mcux_lpc_ctimer_channel_data { counter_alarm_callback_t alarm_callback; @@ -21,6 +22,8 @@ struct mcux_lpc_ctimer_channel_data { struct mcux_lpc_ctimer_data { struct mcux_lpc_ctimer_channel_data channels[NUM_CHANNELS]; + counter_top_callback_t top_callback; + void *top_user_data; }; struct mcux_lpc_ctimer_config { @@ -64,6 +67,19 @@ static int mcux_lpc_ctimer_get_value(const struct device *dev, uint32_t *ticks) return 0; } +static uint32_t mcux_lpc_ctimer_get_top_value(const struct device *dev) +{ + const struct mcux_lpc_ctimer_config *config = dev->config; + CTIMER_Type *base = config->base; + + /* Return the top value if it has been set, else return the max top value */ + if (base->MR[NUM_CHANNELS] != 0) { + return base->MR[NUM_CHANNELS]; + } else { + return config->info.max_top_value; + } +} + static int mcux_lpc_ctimer_set_alarm(const struct device *dev, uint8_t chan_id, const struct counter_alarm_cfg *alarm_cfg) { @@ -73,6 +89,10 @@ static int mcux_lpc_ctimer_set_alarm(const struct device *dev, uint8_t chan_id, uint32_t ticks = alarm_cfg->ticks; uint32_t current = mcux_lpc_ctimer_read(config->base); + if (alarm_cfg->ticks > mcux_lpc_ctimer_get_top_value(dev)) { + return -EINVAL; + } + if (data->channels[chan_id].alarm_callback != NULL) { LOG_ERR("channel already in use"); return -EBUSY; @@ -113,9 +133,33 @@ static int mcux_lpc_ctimer_cancel_alarm(const struct device *dev, uint8_t chan_i static int mcux_lpc_ctimer_set_top_value(const struct device *dev, const struct counter_top_cfg *cfg) { - ARG_UNUSED(dev); - ARG_UNUSED(cfg); - return -ENOTSUP; + const struct mcux_lpc_ctimer_config *config = dev->config; + struct mcux_lpc_ctimer_data *data = dev->data; + bool counter_reset = true; + bool counter_interrupt = true; + + data->top_callback = cfg->callback; + data->top_user_data = cfg->user_data; + + if (cfg->flags & COUNTER_TOP_CFG_DONT_RESET) { + counter_reset = false; + } + + /* If top value specified is 0, then turn off the interrupt */ + if (cfg->ticks == 0) { + counter_interrupt = false; + } + + ctimer_match_config_t match_config = { .matchValue = cfg->ticks, + .enableCounterReset = counter_reset, + .enableCounterStop = false, + .outControl = kCTIMER_Output_NoAction, + .outPinInitState = false, + .enableInterrupt = counter_interrupt }; + + CTIMER_SetupMatch(config->base, NUM_CHANNELS, &match_config); + + return 0; } static uint32_t mcux_lpc_ctimer_get_pending_int(const struct device *dev) @@ -125,13 +169,6 @@ static uint32_t mcux_lpc_ctimer_get_pending_int(const struct device *dev) return (CTIMER_GetStatusFlags(config->base) & 0xF) != 0; } -static uint32_t mcux_lpc_ctimer_get_top_value(const struct device *dev) -{ - const struct mcux_lpc_ctimer_config *config = dev->config; - - return config->info.max_top_value; -} - static uint32_t mcux_lpc_ctimer_get_freq(const struct device *dev) { /* @@ -180,6 +217,10 @@ static void mcux_lpc_ctimer_isr(const struct device *dev) alarm_callback(dev, chan, ticks, alarm_user_data); } } + + if (((interrupt_stat & (0x01 << NUM_CHANNELS)) != 0) && data->top_callback) { + data->top_callback(dev, data->top_user_data); + } } static int mcux_lpc_ctimer_init(const struct device *dev)