Skip to content

NXP driver: lptmr: add alarm and guard period API functions. #93302

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
137 changes: 135 additions & 2 deletions drivers/counter/counter_mcux_lptmr.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
#include <zephyr/irq.h>
#include <fsl_lptmr.h>

#define NUM_CHANNELS 1

struct mcux_lptmr_config {
struct counter_config_info info;
LPTMR_Type *base;
Expand All @@ -22,11 +24,15 @@ struct mcux_lptmr_config {
lptmr_pin_select_t pin;
lptmr_pin_polarity_t polarity;
void (*irq_config_func)(const struct device *dev);
unsigned int irqn;
};

struct mcux_lptmr_data {
counter_top_callback_t top_callback;
counter_alarm_callback_t alarm_callback;
void *top_user_data;
void *alarm_user_data;
uint32_t guard_period;
};

static int mcux_lptmr_start(const struct device *dev)
Expand Down Expand Up @@ -118,11 +124,132 @@ static void mcux_lptmr_isr(const struct device *dev)
if (data->top_callback) {
data->top_callback(dev, data->top_user_data);
}

if (data->alarm_callback) {
uint32_t ticks = LPTMR_GetCurrentTimerCount(config->base);
counter_alarm_callback_t alarm_callback = data->alarm_callback;
void *alarm_user_data = data->alarm_user_data;

data->alarm_callback = NULL;
data->alarm_user_data = NULL;
alarm_callback(dev, 0, ticks, alarm_user_data);
}
}

static int mcux_lptmr_set_guard_period(const struct device *dev, uint32_t guard, uint32_t flags)
{
struct mcux_lptmr_data *data = dev->data;

ARG_UNUSED(flags);

__ASSERT_NO_MSG(guard < mcux_lptmr_get_top_value(dev));
data->guard_period = guard;

return 0;
}

static uint32_t mcux_lptmr_get_guard_period(const struct device *dev, uint32_t flags)
{
struct mcux_lptmr_data *data = dev->data;

ARG_UNUSED(flags);

return data->guard_period;
}

static int mcux_lptmr_set_alarm(const struct device *dev, uint8_t chan_id,
const struct counter_alarm_cfg *alarm_cfg)
{
const struct mcux_lptmr_config *config = dev->config;
struct mcux_lptmr_data *data = dev->data;
uint32_t current;
uint32_t max_rel_val;
bool irq_on_late = false;
uint32_t ticks = alarm_cfg->ticks;
bool absolute = alarm_cfg->flags & COUNTER_ALARM_CFG_ABSOLUTE;
int err = 0;
uint32_t top = mcux_lptmr_get_top_value(dev);

if (alarm_cfg->ticks > top) {
return -EINVAL;
}

if (chan_id != 0) {
return -EINVAL;
}

if (data->alarm_callback) {
return -EBUSY;
}

data->alarm_callback = alarm_cfg->callback;
data->alarm_user_data = alarm_cfg->user_data;

current = LPTMR_GetCurrentTimerCount(config->base);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

are you using the same counter register for both top callback and alarms? both APIs can be used simultaneously, so I don't think that's a good idea

max_rel_val = (current + data->guard_period) % config->info.max_top_value;

if (absolute == 0) {
/*
* If relative value is smaller than half of the counter range it is assumed
* that there is a risk of setting value too late and late detection algorithm
* must be applied. When late setting is detected, interrupt shall be
* triggered for immediate expiration of the timer. Detection is performed
* by limiting relative distance between CMP and CNT.
*
* Note that half of counter range is an arbitrary value.
*/
irq_on_late = ticks < (config->info.max_top_value / 2);
ticks = (ticks + current) % config->info.max_top_value;
} else {
irq_on_late = alarm_cfg->flags & COUNTER_ALARM_CFG_EXPIRE_WHEN_LATE;
ticks = alarm_cfg->ticks % config->info.max_top_value;
}

/*
* When LPTMR is enabled, the CMR can be altered only when CSR[TCF] = 1, since this cannot
* be guaranteed, the timer must be stopped before setting the CMR.
*/
LPTMR_StopTimer(config->base);

LPTMR_SetTimerPeriod(config->base, ticks);
LPTMR_EnableInterrupts(config->base, kLPTMR_TimerInterruptEnable);

LPTMR_StartTimer(config->base);

if (ticks <= max_rel_val) {
if (absolute) {
err = -ETIME;
}

if (irq_on_late) {
NVIC_SetPendingIRQ(config->irqn);
} else {
data->alarm_callback = NULL;
}
}

return err;
}

static int mcux_lptmr_cancel_alarm(const struct device *dev, uint8_t chan_id)
{
const struct mcux_lptmr_config *config = dev->config;
struct mcux_lptmr_data *data = dev->data;

if (chan_id != 0) {
return -EINVAL;
}

LPTMR_DisableInterrupts(config->base, kLPTMR_TimerInterruptEnable);
data->alarm_callback = NULL;

return 0;
}

static int mcux_lptmr_init(const struct device *dev)
{
const struct mcux_lptmr_config *config = dev->config;
struct mcux_lptmr_data *data = dev->data;
lptmr_config_t lptmr_config;

LPTMR_GetDefaultConfig(&lptmr_config);
Expand All @@ -143,6 +270,8 @@ static int mcux_lptmr_init(const struct device *dev)

config->irq_config_func(dev);

data->guard_period = 0;

return 0;
}

Expand All @@ -153,6 +282,10 @@ static DEVICE_API(counter, mcux_lptmr_driver_api) = {
.set_top_value = mcux_lptmr_set_top_value,
.get_pending_int = mcux_lptmr_get_pending_int,
.get_top_value = mcux_lptmr_get_top_value,
.set_alarm = mcux_lptmr_set_alarm,
.cancel_alarm = mcux_lptmr_cancel_alarm,
.set_guard_period = mcux_lptmr_set_guard_period,
.get_guard_period = mcux_lptmr_get_guard_period,
};

#define COUNTER_MCUX_LPTMR_DEVICE_INIT(n) \
Expand Down Expand Up @@ -181,7 +314,7 @@ static DEVICE_API(counter, mcux_lptmr_driver_api) = {
.freq = DT_INST_PROP(n, clock_frequency) / \
DT_INST_PROP(n, prescaler), \
.flags = COUNTER_CONFIG_INFO_COUNT_UP, \
.channels = 0, \
.channels = NUM_CHANNELS, \
}, \
.base = (LPTMR_Type *)DT_INST_REG_ADDR(n), \
.clk_source = DT_INST_PROP(n, clk_source), \
Expand All @@ -193,6 +326,7 @@ static DEVICE_API(counter, mcux_lptmr_driver_api) = {
.prescaler_glitch = DT_INST_PROP(n, prescale_glitch_filter) + \
DT_INST_PROP(n, timer_mode_sel) - 1, \
.irq_config_func = mcux_lptmr_irq_config_##n, \
.irqn = DT_INST_IRQN(n), \
}; \
\
DEVICE_DT_INST_DEFINE(n, &mcux_lptmr_init, NULL, \
Expand All @@ -201,5 +335,4 @@ static DEVICE_API(counter, mcux_lptmr_driver_api) = {
POST_KERNEL, CONFIG_COUNTER_INIT_PRIORITY, \
&mcux_lptmr_driver_api);


DT_INST_FOREACH_STATUS_OKAY(COUNTER_MCUX_LPTMR_DEVICE_INIT)