/* * Copyright 2020 NXP * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT nxp_kinetis_pit #include #include #define LOG_MODULE_NAME counter_pit #include 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; void (*irq_config_func)(const struct device *dev); }; struct mcux_pit_data { counter_alarm_callback_t alarm_callback; counter_top_callback_t top_callback; void *alarm_user_data; 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 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; uint32_t current = 0; 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); } if (data->alarm_callback) { PIT_StopTimer(config->base, config->pit_channel); mcux_pit_get_value(dev, ¤t); data->alarm_callback(dev, config->pit_channel, current, data->alarm_user_data); } } static int mcux_pit_set_alarm(const struct device *dev, uint8_t chan_id, const struct counter_alarm_cfg *alarm_cfg) { const struct mcux_pit_config *config = dev->config; struct mcux_pit_data *data = dev->data; uint32_t ticks = alarm_cfg->ticks; if (chan_id != DT_INST_PROP(0, pit_channel)) { LOG_ERR("Invalid channel id"); return -EINVAL; } if (ticks > mcux_pit_get_top_value(dev)) { LOG_ERR("Invalid ticks"); return -EINVAL; } PIT_StopTimer(config->base, chan_id); PIT_SetTimerPeriod(config->base, chan_id, ticks); data->alarm_callback = alarm_cfg->callback; data->alarm_user_data = alarm_cfg->user_data; LOG_DBG("set alarm to %d", ticks); PIT_StartTimer(config->base, chan_id); return 0; } static int mcux_pit_cancel_alarm(const struct device *dev, uint8_t chan_id) { const struct mcux_pit_config *config = dev->config; struct mcux_pit_data *data = dev->data; if (chan_id != DT_INST_PROP(0, pit_channel)) { LOG_ERR("Invalid channel id"); return -EINVAL; } PIT_DisableInterrupts(config->base, chan_id, kPIT_TimerInterruptEnable); PIT_StopTimer(config->base, chan_id); data->alarm_callback = NULL; return 0; } static int mcux_pit_init(const struct device *dev) { const struct mcux_pit_config *config = (struct mcux_pit_config *)dev->config; pit_config_t pit_config; PIT_GetDefaultConfig(&pit_config); pit_config.enableRunInDebug = config->enableRunInDebug; PIT_Init(config->base, &pit_config); config->irq_config_func(dev); PIT_SetTimerPeriod(config->base, config->pit_channel, USEC_TO_COUNT(DT_INST_PROP(0, pit_period), CLOCK_GetFreq(kCLOCK_BusClk))); 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, .set_alarm = mcux_pit_set_alarm, .cancel_alarm = mcux_pit_cancel_alarm, .get_pending_int = mcux_pit_get_pending_int, .get_top_value = mcux_pit_get_top_value, }; /* * This driver is single-instance. If the devicetree contains multiple * instances, this will fail and the driver needs to be revisited. */ BUILD_ASSERT(DT_NUM_INST_STATUS_OKAY(DT_DRV_COMPAT) <= 1, "unsupported pit instance"); static struct mcux_pit_data mcux_pit_data_0; static void mcux_pit_irq_config_0(const struct device *dev); static const struct mcux_pit_config mcux_pit_config_0 = { .info = { .max_top_value = UINT32_MAX, .channels = 1, .freq = DT_INST_PROP(0, clock_frequency), }, .base = (PIT_Type *)DT_INST_REG_ADDR(0), .pit_channel = DT_INST_PROP(0, pit_channel), .irq_config_func = mcux_pit_irq_config_0, }; DEVICE_DT_INST_DEFINE(0, &mcux_pit_init, NULL, &mcux_pit_data_0, &mcux_pit_config_0, POST_KERNEL, CONFIG_COUNTER_INIT_PRIORITY, &mcux_pit_driver_api); static void mcux_pit_irq_config_0(const struct device *dev) { IRQ_CONNECT(DT_INST_IRQ_BY_IDX(0, 0, irq), DT_INST_IRQ_BY_IDX(0, 0, priority), mcux_pit_isr, DEVICE_DT_INST_GET(0), 0); irq_enable(DT_INST_IRQ_BY_IDX(0, 0, irq)); IRQ_CONNECT(DT_INST_IRQ_BY_IDX(0, 1, irq), DT_INST_IRQ_BY_IDX(0, 1, priority), mcux_pit_isr, DEVICE_DT_INST_GET(0), 0); irq_enable(DT_INST_IRQ_BY_IDX(0, 1, irq)); IRQ_CONNECT(DT_INST_IRQ_BY_IDX(0, 2, irq), DT_INST_IRQ_BY_IDX(0, 2, priority), mcux_pit_isr, DEVICE_DT_INST_GET(0), 0); irq_enable(DT_INST_IRQ_BY_IDX(0, 2, irq)); IRQ_CONNECT(DT_INST_IRQ_BY_IDX(0, 3, irq), DT_INST_IRQ_BY_IDX(0, 3, priority), mcux_pit_isr, DEVICE_DT_INST_GET(0), 0); irq_enable(DT_INST_IRQ_BY_IDX(0, 3, irq)); }