Skip to content
Merged
Show file tree
Hide file tree
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
2 changes: 2 additions & 0 deletions boards/arm/lpcxpresso55s69/doc/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@ features:
+-----------+------------+-------------------------------------+
| USB | on-chip | USB device |
+-----------+------------+-------------------------------------+
| COUNTER | on-chip | counter |
+-----------+------------+-------------------------------------+
Copy link
Contributor

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 COUNTER to CTIMER


Targets available
==================
Expand Down
20 changes: 20 additions & 0 deletions boards/arm/lpcxpresso55s69/lpcxpresso55s69_cpu0.dts
Original file line number Diff line number Diff line change
Expand Up @@ -109,3 +109,23 @@
zephyr_udc0: &usbhs {
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we add this change to lpcxpresso55s69_ns.dts

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point. Should the config option be also added to Kconfig.defconfig.lpc55S69_cpu1? I am guessing that the ctimers are also addressable for cpu1?

Also since the compile support for the imxrt6xx group of chips was added it should probably be added into the code. I don't actually know which specific boards are based on the imxrt6xx which is why I haven't added the configuration details for the ctimer/counter to be enabled with them.

Copy link
Member

Choose a reason for hiding this comment

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

Don't worry about rt6xx in the main repo yet, we will follow up with another PR. I asked you to go ahead and extend your hal_nxp PR to include rt6xx now because that change is really simple and then we won't have to update west.yml again in the follow-up PR.

Copy link
Contributor

Choose a reason for hiding this comment

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

I am not sure if we should add to cpu1. For now we can add to _ns.dts file. Also could you please rebase to the latest mainline code to see if it addresses the CI build error.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Updated the _ns.dts and _ns.yaml files and rebased.

The build error seems to be related to the clock_control_mcux_syscon.c file. And something to do with the mcux_lpc_syscon_clock_control_get_subsys_rate and mcux_lpc_syscon_clock_control_init functions.

status = "okay";
};

&ctimer0 {
status = "okay";
};

&ctimer1 {
status = "okay";
};

&ctimer2 {
status = "okay";
};

&ctimer3 {
status = "okay";
};

&ctimer4 {
status = "okay";
};
1 change: 1 addition & 0 deletions boards/arm/lpcxpresso55s69/lpcxpresso55s69_cpu0.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ supported:
- arduino_i2c
- arduino_serial
- arduino_spi
- counter
- gpio
- i2c
- spi
Expand Down
20 changes: 20 additions & 0 deletions boards/arm/lpcxpresso55s69/lpcxpresso55s69_ns.dts
Original file line number Diff line number Diff line change
Expand Up @@ -107,3 +107,23 @@
zephyr_udc0: &usbhs {
status = "okay";
};

&ctimer0 {
status = "okay";
};

&ctimer1 {
status = "okay";
};

&ctimer2 {
status = "okay";
};

&ctimer3 {
status = "okay";
};

&ctimer4 {
status = "okay";
};
1 change: 1 addition & 0 deletions boards/arm/lpcxpresso55s69/lpcxpresso55s69_ns.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ toolchain:
supported:
- adc
- arduino_spi
- counter
- dma
- gpio
- spi
Expand Down
20 changes: 19 additions & 1 deletion drivers/clock_control/clock_control_mcux_syscon.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this #endif should move after line 99 so the closing brace for the switch command is available for all test cases.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Didn't spot that. Fixed it now.

}
#endif
Expand Down
1 change: 1 addition & 0 deletions drivers/counter/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ zephyr_library_sources_ifdef(CONFIG_TIMER_TMR_CMSDK_APB timer_tmr_cmsdk_
zephyr_library_sources_ifdef(CONFIG_TIMER_DTMR_CMSDK_APB timer_dtmr_cmsdk_apb.c)
zephyr_library_sources_ifdef(CONFIG_COUNTER_GECKO_RTCC counter_gecko_rtcc.c)
zephyr_library_sources_ifdef(CONFIG_COUNTER_IMX_EPIT counter_imx_epit.c)
zephyr_library_sources_ifdef(CONFIG_COUNTER_MCUX_CTIMER counter_mcux_ctimer.c)
zephyr_library_sources_ifdef(CONFIG_COUNTER_MCUX_RTC counter_mcux_rtc.c)
zephyr_library_sources_ifdef(CONFIG_COUNTER_MCUX_LPC_RTC counter_mcux_lpc_rtc.c)
zephyr_library_sources_ifdef(CONFIG_COUNTER_NRF_TIMER counter_nrfx_timer.c)
Expand Down
2 changes: 2 additions & 0 deletions drivers/counter/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -54,4 +54,6 @@ source "drivers/counter/Kconfig.esp32"

source "drivers/counter/Kconfig.mcp7940n"

source "drivers/counter/Kconfig.mcux_ctimer"

endif # COUNTER
10 changes: 10 additions & 0 deletions drivers/counter/Kconfig.mcux_ctimer
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.
251 changes: 251 additions & 0 deletions drivers/counter/counter_mcux_ctimer.c
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;
}

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)
Loading