diff --git a/drivers/counter/CMakeLists.txt b/drivers/counter/CMakeLists.txt index 04ea78fdd96..1fd17b77af2 100644 --- a/drivers/counter/CMakeLists.txt +++ b/drivers/counter/CMakeLists.txt @@ -59,3 +59,4 @@ zephyr_library_sources_ifdef(CONFIG_COUNTER_REALTEK_RTS5912_SLWTMR counter_r zephyr_library_sources_ifdef(CONFIG_COUNTER_REALTEK_RTS5912 counter_realtek_rts5912.c) zephyr_library_sources_ifdef(CONFIG_COUNTER_NEORV32_GPTMR counter_neorv32_gptmr.c) zephyr_library_sources_ifdef(CONFIG_COUNTER_WUT_MAX32 counter_max32_wut.c) +zephyr_library_sources_ifdef(CONFIG_COUNTER_CC23X0_RTC counter_cc23x0_rtc.c) diff --git a/drivers/counter/Kconfig b/drivers/counter/Kconfig index 409448690f2..ff2a8f2498d 100644 --- a/drivers/counter/Kconfig +++ b/drivers/counter/Kconfig @@ -116,4 +116,6 @@ source "drivers/counter/Kconfig.neorv32" source "drivers/counter/Kconfig.max32_wut" +source "drivers/counter/Kconfig.cc23x0_rtc" + endif # COUNTER diff --git a/drivers/counter/Kconfig.cc23x0_rtc b/drivers/counter/Kconfig.cc23x0_rtc new file mode 100644 index 00000000000..5afcbda2d3e --- /dev/null +++ b/drivers/counter/Kconfig.cc23x0_rtc @@ -0,0 +1,9 @@ +# Copyright (c) 2024 BayLibre, SAS +# SPDX-License-Identifier: Apache-2.0 + +config COUNTER_CC23X0_RTC + bool "CC23x0 Counter driver based on the RTC Timer" + default y + depends on DT_HAS_TI_CC23X0_RTC_ENABLED + help + Enable counter driver based on RTC timer for cc23x0 diff --git a/drivers/counter/counter_cc23x0_rtc.c b/drivers/counter/counter_cc23x0_rtc.c new file mode 100644 index 00000000000..d651426bce8 --- /dev/null +++ b/drivers/counter/counter_cc23x0_rtc.c @@ -0,0 +1,241 @@ +/* + * Copyright (c) 2024 BayLibre, SAS + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT ti_cc23x0_rtc + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +LOG_MODULE_REGISTER(cc23x0_counter_rtc, CONFIG_COUNTER_LOG_LEVEL); + +static void counter_cc23x0_isr(const struct device *dev); + +struct counter_cc23x0_config { + struct counter_config_info counter_info; + uint32_t base; +}; + +struct counter_cc23x0_data { + struct counter_alarm_cfg alarm_cfg0; +}; + +static int counter_cc23x0_get_value(const struct device *dev, uint32_t *ticks) +{ + /* Resolution is 8us and max timeout ~9.5h */ + const struct counter_cc23x0_config *config = dev->config; + + *ticks = HWREG(config->base + RTC_O_TIME8U); + + return 0; +} + +static int counter_cc23x0_get_value_64(const struct device *dev, uint64_t *ticks) +{ + const struct counter_cc23x0_config *config = dev->config; + + /* + * RTC counter register is 67 bits, only part of the bits are accessible. + * They are split in two partially overlapping registers: + * TIME524M [50:19] + * TIME8U [34:3] + */ + + uint64_t rtc_time_now = + ((HWREG(config->base + RTC_O_TIME524M) << 16) & 0xFFFFFFF800000000) | + HWREG(config->base + RTC_O_TIME8U); + + *ticks = rtc_time_now; + + return 0; +} + +static void counter_cc23x0_isr(const struct device *dev) +{ + const struct counter_cc23x0_config *config = dev->config; + struct counter_cc23x0_data *data = dev->data; + + /* Clear RTC interrupt regs */ + HWREG(config->base + RTC_O_ICLR) = 0x3; + HWREG(config->base + RTC_O_IMCLR) = 0x3; + + uint32_t now = HWREG(config->base + RTC_O_TIME8U); + + if (data->alarm_cfg0.callback) { + data->alarm_cfg0.callback(dev, 0, now, data->alarm_cfg0.user_data); + } +} + +static int counter_cc23x0_set_alarm(const struct device *dev, uint8_t chan_id, + const struct counter_alarm_cfg *alarm_cfg) +{ + const struct counter_cc23x0_config *config = dev->config; + struct counter_cc23x0_data *data = dev->data; + + /* RTC have resolutiuon of 8us */ + if (counter_ticks_to_us(dev, alarm_cfg->ticks) <= 8) { + return -ENOTSUP; + } + + uint32_t now = HWREG(config->base + RTC_O_TIME8U); + + /* Calculate next alarm relative to current time in us */ + uint32_t next_alarm = now + (counter_ticks_to_us(dev, alarm_cfg->ticks) / 8); + + HWREG(config->base + RTC_O_CH0CC8U) = next_alarm; + HWREG(config->base + RTC_O_IMASK) = 0x1; + HWREG(config->base + RTC_O_ARMSET) = 0x1; + + HWREG(EVTSVT_BASE + EVTSVT_O_CPUIRQ3SEL) = EVTSVT_CPUIRQ16SEL_PUBID_AON_RTC_COMB; + + IRQ_CONNECT(DT_INST_IRQN(0), DT_INST_IRQ(0, priority), counter_cc23x0_isr, + DEVICE_DT_INST_GET(0), 0); + + irq_enable(DT_INST_IRQN(0)); + + data->alarm_cfg0.flags = 0; + data->alarm_cfg0.ticks = alarm_cfg->ticks; + data->alarm_cfg0.callback = alarm_cfg->callback; + data->alarm_cfg0.user_data = alarm_cfg->user_data; + + return 0; +} + +static int counter_cc23x0_cancel_alarm(const struct device *dev, uint8_t chan_id) +{ + const struct counter_cc23x0_config *config = dev->config; + + /* Unset interrupt source */ + HWREG(EVTSVT_BASE + EVTSVT_O_CPUIRQ3SEL) = 0x0; + + /* Unarm both channels */ + HWREG(config->base + RTC_O_ARMCLR) = 0x3; + + return 0; +} + +static int counter_cc23x0_set_top_value(const struct device *dev, const struct counter_top_cfg *cfg) +{ + return -ENOTSUP; +} + +static uint32_t counter_cc23x0_get_pending_int(const struct device *dev) +{ + const struct counter_cc23x0_config *config = dev->config; + struct counter_cc23x0_data *data = dev->data; + + /* Check interrupt and mask */ + if (HWREG(config->base + RTC_O_RIS) & HWREG(config->base + RTC_O_MIS)) { + /* Clear RTC interrupt regs */ + HWREG(config->base + RTC_O_ICLR) = 0x3; + HWREG(config->base + RTC_O_IMCLR) = 0x3; + + uint32_t now = HWREG(config->base + RTC_O_TIME8U); + + if (data->alarm_cfg0.callback) { + data->alarm_cfg0.callback(dev, 0, now, data->alarm_cfg0.user_data); + } + + return 0; + } + + return -ESRCH; +} + +static uint32_t counter_cc23x0_get_top_value(const struct device *dev) +{ + ARG_UNUSED(dev); + + return -ENOTSUP; +} + +static uint32_t counter_cc23x0_get_freq(const struct device *dev) +{ + ARG_UNUSED(dev); + + /* + * From TRM clock for RTC is 24Mhz handled internally + * which is 1/2 from main 48Mhz clock = 24Mhz + * Accessible for user resolution is 8us per bit + * TIME8U [34:3] ~ 9.5h + */ + + return (DT_PROP(DT_PATH(cpus, cpu_0), clock_frequency) / 2); +} + +static int counter_cc23x0_start(const struct device *dev) +{ + ARG_UNUSED(dev); + + /* RTC timer runs after power-on reset */ + + return -ENOTSUP; +} + +static int counter_cc23x0_stop(const struct device *dev) +{ + ARG_UNUSED(dev); + + /* Any reset/sleep mode, except for POR, will not stop or reset the RTC timer */ + + return -ENOTSUP; +} + +static int counter_cc23x0_init(const struct device *dev) +{ + const struct counter_cc23x0_config *config = dev->config; + + /* Clear interrupt Mask */ + HWREG(config->base + RTC_O_IMCLR) = 0x3; + + /* Clear Interrupt */ + HWREG(config->base + RTC_O_ICLR) = 0x3; + + /* Clear Armed */ + HWREG(config->base + RTC_O_ARMCLR) = 0x3; + + return 0; +} + +static const struct counter_driver_api rtc_cc23x0_api = { + .start = counter_cc23x0_start, + .stop = counter_cc23x0_stop, + .get_value = counter_cc23x0_get_value, + .get_value_64 = counter_cc23x0_get_value_64, + .set_alarm = counter_cc23x0_set_alarm, + .cancel_alarm = counter_cc23x0_cancel_alarm, + .get_top_value = counter_cc23x0_get_top_value, + .set_top_value = counter_cc23x0_set_top_value, + .get_pending_int = counter_cc23x0_get_pending_int, + .get_freq = counter_cc23x0_get_freq, +}; + +#define CC23X0_INIT(inst) \ + static const struct counter_cc23x0_config cc23x0_config_##inst = { \ + .counter_info = \ + { \ + .max_top_value = UINT32_MAX, \ + .flags = COUNTER_CONFIG_INFO_COUNT_UP, \ + .channels = 1, \ + }, \ + .base = DT_INST_REG_ADDR(inst), \ + }; \ + \ + static struct counter_cc23x0_data cc23x0_data_##inst; \ + \ + DEVICE_DT_INST_DEFINE(0, &counter_cc23x0_init, NULL, &cc23x0_data_##inst, \ + &cc23x0_config_##inst, POST_KERNEL, CONFIG_COUNTER_INIT_PRIORITY, \ + &rtc_cc23x0_api); + +DT_INST_FOREACH_STATUS_OKAY(CC23X0_INIT) diff --git a/dts/bindings/counter/ti,cc23x0-rtc.yaml b/dts/bindings/counter/ti,cc23x0-rtc.yaml new file mode 100644 index 00000000000..e6b046c378f --- /dev/null +++ b/dts/bindings/counter/ti,cc23x0-rtc.yaml @@ -0,0 +1,15 @@ +# Copyright (c) 2024 BayLibre, SAS +# SPDX-License-Identifier: Apache-2.0 + +description: | + CC23x0 RTC counter driver + Any reset/sleep mode, except for the power-up reset, will not + stop or reset the RTC Timer. This behavior may be handy when + supporting applications that need to keep a timing baseline on + such situations. + There is also no need to enable the RTC Timer node, it starts + running from power-up. + +compatible: "ti,cc23x0-rtc" + +include: base.yaml