220 lines
6.1 KiB
C
220 lines
6.1 KiB
C
/*
|
|
* Copyright 2020,2023 NXP
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#define DT_DRV_COMPAT nxp_kinetis_pit
|
|
|
|
#include <zephyr/drivers/counter.h>
|
|
#include <zephyr/drivers/clock_control.h>
|
|
#include <zephyr/irq.h>
|
|
#include <fsl_pit.h>
|
|
|
|
#define LOG_MODULE_NAME counter_pit
|
|
#include <zephyr/logging/log.h>
|
|
LOG_MODULE_REGISTER(LOG_MODULE_NAME, CONFIG_COUNTER_LOG_LEVEL);
|
|
|
|
struct mcux_pit_config {
|
|
struct counter_config_info info;
|
|
PIT_Type *base;
|
|
bool enableRunInDebug;
|
|
pit_chnl_t pit_channel;
|
|
uint32_t pit_period;
|
|
void (*irq_config_func)(const struct device *dev);
|
|
const struct device *clock_dev;
|
|
clock_control_subsys_t clock_subsys;
|
|
};
|
|
|
|
struct mcux_pit_data {
|
|
counter_top_callback_t top_callback;
|
|
void *top_user_data;
|
|
};
|
|
|
|
static uint32_t mcux_pit_get_top_value(const struct device *dev)
|
|
{
|
|
const struct mcux_pit_config *config = dev->config;
|
|
pit_chnl_t channel = config->pit_channel;
|
|
|
|
/*
|
|
* According to RM, the LDVAL trigger = clock ticks -1
|
|
* The underlying HAL driver function PIT_SetTimerPeriod()
|
|
* automatically subtracted 1 from the value that ends up in
|
|
* LDVAL so for reporting purposes we need to add it back in
|
|
* here to by consistent.
|
|
*/
|
|
return (config->base->CHANNEL[channel].LDVAL + 1);
|
|
}
|
|
|
|
static int mcux_pit_start(const struct device *dev)
|
|
{
|
|
const struct mcux_pit_config *config = dev->config;
|
|
|
|
LOG_DBG("period is %d", mcux_pit_get_top_value(dev));
|
|
PIT_EnableInterrupts(config->base, config->pit_channel,
|
|
kPIT_TimerInterruptEnable);
|
|
PIT_StartTimer(config->base, config->pit_channel);
|
|
return 0;
|
|
}
|
|
|
|
static int mcux_pit_stop(const struct device *dev)
|
|
{
|
|
const struct mcux_pit_config *config = dev->config;
|
|
|
|
PIT_DisableInterrupts(config->base, config->pit_channel,
|
|
kPIT_TimerInterruptEnable);
|
|
PIT_StopTimer(config->base, config->pit_channel);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mcux_pit_get_value(const struct device *dev, uint32_t *ticks)
|
|
{
|
|
const struct mcux_pit_config *config = dev->config;
|
|
|
|
*ticks = PIT_GetCurrentTimerCount(config->base, config->pit_channel);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int mcux_pit_set_top_value(const struct device *dev,
|
|
const struct counter_top_cfg *cfg)
|
|
{
|
|
const struct mcux_pit_config *config = dev->config;
|
|
struct mcux_pit_data *data = dev->data;
|
|
pit_chnl_t channel = config->pit_channel;
|
|
|
|
if (cfg->ticks == 0) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
data->top_callback = cfg->callback;
|
|
data->top_user_data = cfg->user_data;
|
|
|
|
if (config->base->CHANNEL[channel].TCTRL & PIT_TCTRL_TEN_MASK) {
|
|
/* Timer already enabled, check flags before resetting */
|
|
if (cfg->flags & COUNTER_TOP_CFG_DONT_RESET) {
|
|
return -ENOTSUP;
|
|
}
|
|
PIT_StopTimer(config->base, channel);
|
|
PIT_SetTimerPeriod(config->base, channel, cfg->ticks);
|
|
PIT_StartTimer(config->base, channel);
|
|
} else {
|
|
PIT_SetTimerPeriod(config->base, channel, cfg->ticks);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static uint32_t mcux_pit_get_pending_int(const struct device *dev)
|
|
{
|
|
const struct mcux_pit_config *config = dev->config;
|
|
uint32_t mask = PIT_TFLG_TIF_MASK;
|
|
uint32_t flags;
|
|
|
|
flags = PIT_GetStatusFlags(config->base, config->pit_channel);
|
|
|
|
return ((flags & mask) == mask);
|
|
}
|
|
|
|
static uint32_t mcux_pit_get_frequency(const struct device *dev)
|
|
{
|
|
const struct mcux_pit_config *config = dev->config;
|
|
uint32_t clock_rate;
|
|
|
|
if (clock_control_get_rate(config->clock_dev, config->clock_subsys, &clock_rate)) {
|
|
LOG_ERR("Failed to get clock rate");
|
|
return 0;
|
|
}
|
|
|
|
return clock_rate;
|
|
}
|
|
|
|
static void mcux_pit_isr(const struct device *dev)
|
|
{
|
|
const struct mcux_pit_config *config = dev->config;
|
|
struct mcux_pit_data *data = dev->data;
|
|
uint32_t flags;
|
|
|
|
LOG_DBG("pit counter isr");
|
|
flags = PIT_GetStatusFlags(config->base, config->pit_channel);
|
|
PIT_ClearStatusFlags(config->base, config->pit_channel, flags);
|
|
if (data->top_callback) {
|
|
data->top_callback(dev, data->top_user_data);
|
|
}
|
|
}
|
|
|
|
static int mcux_pit_init(const struct device *dev)
|
|
{
|
|
const struct mcux_pit_config *config = dev->config;
|
|
pit_config_t pit_config;
|
|
uint32_t clock_rate;
|
|
|
|
if (!device_is_ready(config->clock_dev)) {
|
|
LOG_ERR("Clock control device not ready");
|
|
return -ENODEV;
|
|
}
|
|
|
|
PIT_GetDefaultConfig(&pit_config);
|
|
pit_config.enableRunInDebug = config->enableRunInDebug;
|
|
|
|
PIT_Init(config->base, &pit_config);
|
|
|
|
config->irq_config_func(dev);
|
|
|
|
clock_rate = mcux_pit_get_frequency(dev);
|
|
PIT_SetTimerPeriod(config->base, config->pit_channel,
|
|
USEC_TO_COUNT(config->pit_period, clock_rate));
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct counter_driver_api mcux_pit_driver_api = {
|
|
.start = mcux_pit_start,
|
|
.stop = mcux_pit_stop,
|
|
.get_value = mcux_pit_get_value,
|
|
.set_top_value = mcux_pit_set_top_value,
|
|
.get_pending_int = mcux_pit_get_pending_int,
|
|
.get_top_value = mcux_pit_get_top_value,
|
|
.get_freq = mcux_pit_get_frequency,
|
|
};
|
|
|
|
#define COUNTER_MCUX_PIT_IRQ_CONFIG(idx, n) \
|
|
do { \
|
|
IRQ_CONNECT(DT_INST_IRQ_BY_IDX(n, idx, irq), \
|
|
DT_INST_IRQ_BY_IDX(n, idx, priority), \
|
|
mcux_pit_isr, DEVICE_DT_INST_GET(n), \
|
|
COND_CODE_1(DT_INST_IRQ_HAS_NAME(n, flags), \
|
|
(DT_INST_IRQ_BY_IDX(n, idx, flags)), (0))); \
|
|
irq_enable(DT_INST_IRQ_BY_IDX(n, idx, irq)); \
|
|
} while (0)
|
|
|
|
#define COUNTER_MCUX_PIT_DEVICE(n) \
|
|
static void mcux_pit_irq_config_##n(const struct device *dev); \
|
|
static struct mcux_pit_data mcux_pit_data_##n; \
|
|
static const struct mcux_pit_config mcux_pit_config_##n = { \
|
|
.info = { \
|
|
.max_top_value = DT_INST_PROP(n, max_load_value), \
|
|
.channels = 0, \
|
|
}, \
|
|
.base = (PIT_Type *)DT_INST_REG_ADDR(n), \
|
|
.pit_channel = DT_INST_PROP(n, pit_channel), \
|
|
.pit_period = DT_INST_PROP(n, pit_period), \
|
|
.irq_config_func = mcux_pit_irq_config_##n, \
|
|
.clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(n)), \
|
|
.clock_subsys = (clock_control_subsys_t) \
|
|
DT_INST_CLOCKS_CELL(n, name), \
|
|
}; \
|
|
\
|
|
DEVICE_DT_INST_DEFINE(n, &mcux_pit_init, NULL, \
|
|
&mcux_pit_data_##n, &mcux_pit_config_##n, POST_KERNEL, \
|
|
CONFIG_COUNTER_INIT_PRIORITY, &mcux_pit_driver_api); \
|
|
\
|
|
static void mcux_pit_irq_config_##n(const struct device *dev) \
|
|
{ \
|
|
LISTIFY(DT_NUM_IRQS(DT_DRV_INST(n)), \
|
|
COUNTER_MCUX_PIT_IRQ_CONFIG, (;), n); \
|
|
}
|
|
|
|
DT_INST_FOREACH_STATUS_OKAY(COUNTER_MCUX_PIT_DEVICE)
|