- 
                Notifications
    You must be signed in to change notification settings 
- Fork 8.2k
drivers: counter: added ctimer driver for lpcexpresso55s69 #37613
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
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
|  | @@ -109,3 +109,23 @@ | |
| zephyr_udc0: &usbhs { | ||
|          | ||
| status = "okay"; | ||
| }; | ||
|  | ||
| &ctimer0 { | ||
| status = "okay"; | ||
| }; | ||
|  | ||
| &ctimer1 { | ||
| status = "okay"; | ||
| }; | ||
|  | ||
| &ctimer2 { | ||
| status = "okay"; | ||
| }; | ||
|  | ||
| &ctimer3 { | ||
| status = "okay"; | ||
| }; | ||
|  | ||
| &ctimer4 { | ||
| status = "okay"; | ||
| }; | ||
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
|  | @@ -19,6 +19,7 @@ supported: | |
| - arduino_i2c | ||
| - arduino_serial | ||
| - arduino_spi | ||
| - counter | ||
| - gpio | ||
| - i2c | ||
| - spi | ||
|  | ||
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
|  | @@ -17,6 +17,7 @@ toolchain: | |
| supported: | ||
| - adc | ||
| - arduino_spi | ||
| - counter | ||
| - dma | ||
| - gpio | ||
| - spi | ||
|  | ||
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
|  | @@ -34,7 +34,8 @@ static int mcux_lpc_syscon_clock_control_get_subsys_rate( | |
| { | ||
| #if defined(CONFIG_I2C_MCUX_FLEXCOMM) || \ | ||
| defined(CONFIG_SPI_MCUX_FLEXCOMM) || \ | ||
| defined(CONFIG_UART_MCUX_FLEXCOMM) | ||
| defined(CONFIG_UART_MCUX_FLEXCOMM) || \ | ||
| defined(CONFIG_COUNTER_MCUX_CTIMER) | ||
|  | ||
| uint32_t clock_name = (uint32_t) sub_system; | ||
|  | ||
|  | @@ -79,6 +80,23 @@ static int mcux_lpc_syscon_clock_control_get_subsys_rate( | |
| case MCUX_USDHC2_CLK: | ||
| *rate = CLOCK_GetSdioClkFreq(1); | ||
| break; | ||
| #endif | ||
| #if defined(CONFIG_COUNTER_MCUX_CTIMER) | ||
| case (MCUX_CTIMER0_CLK + MCUX_CTIMER_CLK_OFFSET): | ||
| *rate = CLOCK_GetCTimerClkFreq(0); | ||
| break; | ||
| case (MCUX_CTIMER1_CLK + MCUX_CTIMER_CLK_OFFSET): | ||
| *rate = CLOCK_GetCTimerClkFreq(1); | ||
| break; | ||
| case (MCUX_CTIMER2_CLK + MCUX_CTIMER_CLK_OFFSET): | ||
| *rate = CLOCK_GetCTimerClkFreq(2); | ||
| break; | ||
| case (MCUX_CTIMER3_CLK + MCUX_CTIMER_CLK_OFFSET): | ||
| *rate = CLOCK_GetCTimerClkFreq(3); | ||
| break; | ||
| case (MCUX_CTIMER4_CLK + MCUX_CTIMER_CLK_OFFSET): | ||
| *rate = CLOCK_GetCTimerClkFreq(4); | ||
| break; | ||
| #endif | ||
|          | ||
| } | ||
| #endif | ||
|  | ||
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| # MCUXpresso SDK CTIMER | ||
|  | ||
| # Copyright (c) 2021 Toby firth | ||
| # SPDX-License-Identifier: Apache-2.0 | ||
|  | ||
| config COUNTER_MCUX_CTIMER | ||
| bool "MCUX CTIMER driver" | ||
| depends on HAS_MCUX_CTIMER | ||
| help | ||
| Enable support for MCUX CTIMER driver. | 
| Original file line number | Diff line number | Diff line change | 
|---|---|---|
| @@ -0,0 +1,251 @@ | ||
| /* | ||
| * Copyright (c) 2021, Toby Firth. | ||
| * | ||
| * SPDX-License-Identifier: Apache-2.0 | ||
| */ | ||
| #define DT_DRV_COMPAT nxp_lpc_ctimer | ||
|  | ||
| #include <drivers/counter.h> | ||
| #include <fsl_ctimer.h> | ||
| #include <logging/log.h> | ||
| #include <drivers/clock_control.h> | ||
| #include <dt-bindings/clock/mcux_lpc_syscon_clock.h> | ||
| LOG_MODULE_REGISTER(mcux_ctimer, CONFIG_COUNTER_LOG_LEVEL); | ||
|  | ||
| #define NUM_CHANNELS 4 | ||
|  | ||
| struct mcux_lpc_ctimer_channel_data { | ||
| counter_alarm_callback_t alarm_callback; | ||
| void *alarm_user_data; | ||
| }; | ||
|  | ||
| struct mcux_lpc_ctimer_data { | ||
| struct mcux_lpc_ctimer_channel_data channels[NUM_CHANNELS]; | ||
| }; | ||
|  | ||
| struct mcux_lpc_ctimer_config { | ||
| struct counter_config_info info; | ||
| CTIMER_Type *base; | ||
| const struct device *clock_dev; | ||
| clock_control_subsys_t clock_subsys; | ||
| ctimer_timer_mode_t mode; | ||
| ctimer_capture_channel_t input; | ||
| uint32_t prescale; | ||
| void (*irq_config_func)(const struct device *dev); | ||
| }; | ||
|  | ||
| static int mcux_lpc_ctimer_start(const struct device *dev) | ||
| { | ||
| const struct mcux_lpc_ctimer_config *config = dev->config; | ||
|  | ||
| CTIMER_StartTimer(config->base); | ||
|  | ||
| return 0; | ||
| } | ||
|  | ||
| static int mcux_lpc_ctimer_stop(const struct device *dev) | ||
| { | ||
| const struct mcux_lpc_ctimer_config *config = dev->config; | ||
|  | ||
| CTIMER_StopTimer(config->base); | ||
|  | ||
| return 0; | ||
| } | ||
|  | ||
| static uint32_t mcux_lpc_ctimer_read(CTIMER_Type *base) | ||
| { | ||
| return CTIMER_GetTimerCountValue(base); | ||
| } | ||
|  | ||
| static int mcux_lpc_ctimer_get_value(const struct device *dev, uint32_t *ticks) | ||
| { | ||
| const struct mcux_lpc_ctimer_config *config = dev->config; | ||
| *ticks = mcux_lpc_ctimer_read(config->base); | ||
| return 0; | ||
| } | ||
|  | ||
| static int mcux_lpc_ctimer_set_alarm(const struct device *dev, uint8_t chan_id, | ||
| const struct counter_alarm_cfg *alarm_cfg) | ||
| { | ||
| const struct mcux_lpc_ctimer_config *config = dev->config; | ||
| struct mcux_lpc_ctimer_data *data = dev->data; | ||
|  | ||
| uint32_t ticks = alarm_cfg->ticks; | ||
| uint32_t current = mcux_lpc_ctimer_read(config->base); | ||
|  | ||
| if (data->channels[chan_id].alarm_callback != NULL) { | ||
| LOG_ERR("channel already in use"); | ||
| return -EBUSY; | ||
|         
                  MaureenHelm marked this conversation as resolved.
              Outdated
          
            Show resolved
            Hide resolved | ||
| } | ||
|  | ||
| if ((alarm_cfg->flags & COUNTER_ALARM_CFG_ABSOLUTE) == 0) { | ||
| ticks += current; | ||
| } | ||
|  | ||
| data->channels[chan_id].alarm_callback = alarm_cfg->callback; | ||
| data->channels[chan_id].alarm_user_data = alarm_cfg->user_data; | ||
|  | ||
| ctimer_match_config_t match_config = { .matchValue = ticks, | ||
| .enableCounterReset = false, | ||
| .enableCounterStop = false, | ||
| .outControl = kCTIMER_Output_NoAction, | ||
| .outPinInitState = false, | ||
| .enableInterrupt = true }; | ||
|  | ||
| CTIMER_SetupMatch(config->base, chan_id, &match_config); | ||
|  | ||
| return 0; | ||
| } | ||
|  | ||
| static int mcux_lpc_ctimer_cancel_alarm(const struct device *dev, uint8_t chan_id) | ||
| { | ||
| const struct mcux_lpc_ctimer_config *config = dev->config; | ||
| struct mcux_lpc_ctimer_data *data = dev->data; | ||
|  | ||
| CTIMER_DisableInterrupts(config->base, (1 << chan_id)); | ||
|  | ||
| data->channels[chan_id].alarm_callback = NULL; | ||
| data->channels[chan_id].alarm_user_data = NULL; | ||
|  | ||
| return 0; | ||
| } | ||
|  | ||
| 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; | ||
| } | ||
|  | ||
| static uint32_t mcux_lpc_ctimer_get_pending_int(const struct device *dev) | ||
| { | ||
| const struct mcux_lpc_ctimer_config *config = dev->config; | ||
|  | ||
| 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 void mcux_lpc_ctimer_isr(const struct device *dev) | ||
| { | ||
| const struct mcux_lpc_ctimer_config *config = dev->config; | ||
| struct mcux_lpc_ctimer_data *data = dev->data; | ||
|  | ||
| uint32_t interrupt_stat = CTIMER_GetStatusFlags(config->base); | ||
|  | ||
| CTIMER_ClearStatusFlags(config->base, interrupt_stat); | ||
|  | ||
| uint32_t ticks = mcux_lpc_ctimer_read(config->base); | ||
|  | ||
| for (uint8_t chan = 0; chan < NUM_CHANNELS; chan++) { | ||
| uint8_t channel_mask = 0x01 << chan; | ||
|  | ||
| if (((interrupt_stat & channel_mask) != 0) && | ||
| (data->channels[chan].alarm_callback != NULL)) { | ||
| counter_alarm_callback_t alarm_callback = | ||
| data->channels[chan].alarm_callback; | ||
| void *alarm_user_data = data->channels[chan].alarm_user_data; | ||
|  | ||
| data->channels[chan].alarm_callback = NULL; | ||
| data->channels[chan].alarm_user_data = NULL; | ||
| alarm_callback(dev, chan, ticks, alarm_user_data); | ||
| } | ||
| } | ||
| } | ||
|  | ||
| static int mcux_lpc_ctimer_init(const struct device *dev) | ||
| { | ||
| /* | ||
| * The frequency of the timer is not known at compile time so we need to | ||
| * modify the timer's config in the init function at runtime when the | ||
| * frequency is known. | ||
| */ | ||
| struct mcux_lpc_ctimer_config *config = (struct mcux_lpc_ctimer_config *)dev->config; | ||
| struct mcux_lpc_ctimer_data *data = dev->data; | ||
|  | ||
| ctimer_config_t ctimer_config; | ||
|  | ||
| uint32_t clk_freq = 0; | ||
|  | ||
| if (clock_control_get_rate(config->clock_dev, config->clock_subsys, | ||
| &clk_freq)) { | ||
| LOG_ERR("unable to get clock frequency"); | ||
| return -EINVAL; | ||
| } | ||
|  | ||
| /* prescale increments when the prescale counter is 0 so if prescale is 1 | ||
| * the counter is incremented every 2 cycles of the clock so will actually | ||
| * divide by 2 hence the addition of 1 to the value here. | ||
| */ | ||
| uint32_t freq = clk_freq / (config->prescale + 1); | ||
|  | ||
| config->info.freq = freq; | ||
|  | ||
| for (uint8_t chan = 0; chan < NUM_CHANNELS; chan++) { | ||
| data->channels[chan].alarm_callback = NULL; | ||
| data->channels[chan].alarm_user_data = NULL; | ||
| } | ||
|  | ||
| CTIMER_GetDefaultConfig(&ctimer_config); | ||
|  | ||
| ctimer_config.mode = config->mode; | ||
| ctimer_config.input = config->input; | ||
| ctimer_config.prescale = config->prescale; | ||
|  | ||
| CTIMER_Init(config->base, &ctimer_config); | ||
|  | ||
| config->irq_config_func(dev); | ||
|  | ||
| return 0; | ||
| } | ||
|  | ||
| static const struct counter_driver_api mcux_ctimer_driver_api = { | ||
| .start = mcux_lpc_ctimer_start, | ||
| .stop = mcux_lpc_ctimer_stop, | ||
| .get_value = mcux_lpc_ctimer_get_value, | ||
| .set_alarm = mcux_lpc_ctimer_set_alarm, | ||
| .cancel_alarm = mcux_lpc_ctimer_cancel_alarm, | ||
| .set_top_value = mcux_lpc_ctimer_set_top_value, | ||
| .get_pending_int = mcux_lpc_ctimer_get_pending_int, | ||
| .get_top_value = mcux_lpc_ctimer_get_top_value, | ||
| }; | ||
|  | ||
| #define CTIMER_CLOCK_SOURCE(id) TO_CTIMER_CLOCK_SOURCE(id, DT_INST_PROP(id, clk_source)) | ||
| #define TO_CTIMER_CLOCK_SOURCE(id, val) MUX_A(CM_CTIMERCLKSEL##id, val) | ||
|  | ||
| #define COUNTER_LPC_CTIMER_DEVICE(id) \ | ||
| static void mcux_lpc_ctimer_irq_config_##id(const struct device *dev); \ | ||
| static struct mcux_lpc_ctimer_config mcux_lpc_ctimer_config_##id = { \ | ||
| .info = { \ | ||
| .max_top_value = UINT32_MAX, \ | ||
| .freq = 1, \ | ||
| .flags = COUNTER_CONFIG_INFO_COUNT_UP, \ | ||
| .channels = NUM_CHANNELS, \ | ||
| },\ | ||
| .base = (CTIMER_Type *)DT_INST_REG_ADDR(id), \ | ||
| .clock_dev = DEVICE_DT_GET(DT_INST_CLOCKS_CTLR(id)), \ | ||
| .clock_subsys = \ | ||
| (clock_control_subsys_t)(DT_INST_CLOCKS_CELL(id, name) + MCUX_CTIMER_CLK_OFFSET),\ | ||
| .mode = DT_INST_PROP(id, mode), \ | ||
| .input = DT_INST_PROP(id, input), \ | ||
| .prescale = DT_INST_PROP(id, prescale), \ | ||
| .irq_config_func = mcux_lpc_ctimer_irq_config_##id, \ | ||
| }; \ | ||
| static struct mcux_lpc_ctimer_data mcux_lpc_ctimer_data_##id; \ | ||
| DEVICE_DT_INST_DEFINE(id, &mcux_lpc_ctimer_init, NULL, &mcux_lpc_ctimer_data_##id, \ | ||
| &mcux_lpc_ctimer_config_##id, POST_KERNEL, \ | ||
| CONFIG_KERNEL_INIT_PRIORITY_DEVICE, &mcux_ctimer_driver_api); \ | ||
| static void mcux_lpc_ctimer_irq_config_##id(const struct device *dev) \ | ||
| { \ | ||
| IRQ_CONNECT(DT_INST_IRQN(id), DT_INST_IRQ(id, priority), mcux_lpc_ctimer_isr, \ | ||
| DEVICE_DT_INST_GET(id), 0); \ | ||
| irq_enable(DT_INST_IRQN(id)); \ | ||
| } | ||
|  | ||
| DT_INST_FOREACH_STATUS_OKAY(COUNTER_LPC_CTIMER_DEVICE) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just noticed this, should we change
COUNTERtoCTIMER