diff --git a/drivers/counter/CMakeLists.txt b/drivers/counter/CMakeLists.txt index 93a51369f572..0e709d49ef46 100644 --- a/drivers/counter/CMakeLists.txt +++ b/drivers/counter/CMakeLists.txt @@ -54,3 +54,4 @@ zephyr_library_sources_ifdef(CONFIG_COUNTER_RTC_MAX32 counter_max32_rt zephyr_library_sources_ifdef(CONFIG_COUNTER_NXP_MRT counter_nxp_mrt.c) zephyr_library_sources_ifdef(CONFIG_COUNTER_RA_AGT counter_renesas_ra_agt.c) zephyr_library_sources_ifdef(CONFIG_COUNTER_RENESAS_RZ_GTM counter_renesas_rz_gtm.c) +zephyr_library_sources_ifdef(CONFIG_COUNTER_CC23X0_LGPT counter_cc23x0_lgpt.c) diff --git a/drivers/counter/Kconfig b/drivers/counter/Kconfig index d9d1dcafcc5f..e5bca37a8a96 100644 --- a/drivers/counter/Kconfig +++ b/drivers/counter/Kconfig @@ -106,4 +106,6 @@ source "drivers/counter/Kconfig.renesas_ra" source "drivers/counter/Kconfig.renesas_rz" +source "drivers/counter/Kconfig.cc23x0_lgpt" + endif # COUNTER diff --git a/drivers/counter/Kconfig.cc23x0_lgpt b/drivers/counter/Kconfig.cc23x0_lgpt new file mode 100644 index 000000000000..da90b3170757 --- /dev/null +++ b/drivers/counter/Kconfig.cc23x0_lgpt @@ -0,0 +1,9 @@ +# Copyright (c) 2024 BayLibre, SAS +# SPDX-License-Identifier: Apache-2.0 + +config COUNTER_CC23X0_LGPT + bool "CC23x0 Counter driver based on the LGPT Timer" + default y + depends on DT_HAS_TI_CC23X0_LGPT_ENABLED + help + Enable counter driver based on LGPT timer for cc23x0 diff --git a/drivers/counter/counter_cc23x0_lgpt.c b/drivers/counter/counter_cc23x0_lgpt.c new file mode 100644 index 000000000000..19abb20805f8 --- /dev/null +++ b/drivers/counter/counter_cc23x0_lgpt.c @@ -0,0 +1,302 @@ +/* + * Copyright (c) 2024 BayLibre, SAS + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT ti_cc23x0_lgpt + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +LOG_MODULE_REGISTER(counter_cc23x0_lgpt, CONFIG_COUNTER_LOG_LEVEL); + +static void counter_cc23x0_lgpt_isr(const struct device *dev); + +struct counter_cc23x0_lgpt_config { + struct counter_config_info counter_info; + uint32_t base; + uint32_t prescale; +}; + +struct counter_cc23x0_lgpt_data { + struct counter_alarm_cfg alarm_cfg[3]; + struct counter_top_cfg target_cfg; +}; + +static int counter_cc23x0_lgpt_get_value(const struct device *dev, uint32_t *ticks) +{ + const struct counter_cc23x0_lgpt_config *config = dev->config; + + *ticks = HWREG(config->base + LGPT_O_CNTR); + + return 0; +} + +static void counter_cc23x0_lgpt_isr(const struct device *dev) +{ + const struct counter_cc23x0_lgpt_config *config = dev->config; + const struct counter_cc23x0_lgpt_data *data = dev->data; + uint32_t reg_ris = HWREG(config->base + LGPT_O_RIS); + uint32_t reg_mis = HWREG(config->base + LGPT_O_MIS); + uint32_t isr = reg_ris & reg_mis; + + HWREG(config->base + LGPT_O_ICLR) |= reg_mis; + HWREG(config->base + LGPT_O_IMCLR) |= reg_mis; + + LOG_DBG("\nISR -> LGPT[%x] RIS[%x] MIS[%x] ISR[%x]\n", config->base, reg_ris, reg_mis, isr); + + if (isr & LGPT_RIS_TGT) { + LOG_DBG("LGPT_RIS_TGT\n"); + if (data->target_cfg.callback) { + data->target_cfg.callback(dev, data->target_cfg.user_data); + } + } + if (isr & LGPT_RIS_ZERO) { + LOG_DBG("LGPT_RIS_ZERO\n"); + } + if (isr & LGPT_RIS_DBLTRANS) { + LOG_DBG("LGPT_RIS_DBLTRANS\n"); + } + if (isr & LGPT_RIS_CNTRCHNG) { + LOG_DBG("LGPT_RIS_CNTRCHNG\n"); + } + if (isr & LGPT_RIS_DIRCHNG) { + LOG_DBG("LGPT_RIS_DIRCHNG\n"); + } + if (isr & LGPT_RIS_IDX) { + LOG_DBG("LGPT_RIS_IDX\n"); + } + if (isr & LGPT_RIS_FAULT) { + LOG_DBG("LGPT_RIS_FAULT\n"); + } + if (isr & LGPT_RIS_C0CC) { + LOG_DBG("LGPT_RIS_C0CC\n"); + if (data->alarm_cfg[0].callback) { + data->alarm_cfg[0].callback(dev, 0, HWREG(config->base + LGPT_O_CNTR), + data->alarm_cfg[0].user_data); + } + } + if (isr & LGPT_RIS_C1CC) { + LOG_DBG("LGPT_RIS_C1CC\n"); + if (data->alarm_cfg[1].callback) { + data->alarm_cfg[1].callback(dev, 1, HWREG(config->base + LGPT_O_CNTR), + data->alarm_cfg[1].user_data); + } + } + if (isr & LGPT_RIS_C2CC) { + LOG_DBG("LGPT_RIS_C2CC\n"); + if (data->alarm_cfg[2].callback) { + data->alarm_cfg[2].callback(dev, 2, HWREG(config->base + LGPT_O_CNTR), + data->alarm_cfg[2].user_data); + } + } +} + +static uint32_t counter_cc23x0_lgpt_get_freq(const struct device *dev) +{ + const struct counter_cc23x0_lgpt_config *config = dev->config; + + return (DT_PROP(DT_PATH(cpus, cpu_0), clock_frequency) / config->prescale); +} + +static int counter_cc23x0_lgpt_set_alarm(const struct device *dev, uint8_t chan_id, + const struct counter_alarm_cfg *alarm_cfg) +{ + const struct counter_cc23x0_lgpt_config *config = dev->config; + struct counter_cc23x0_lgpt_data *data = dev->data; + + if (alarm_cfg->ticks > config->counter_info.max_top_value) { + LOG_ERR("Ticks out of range\n"); + return -EINVAL; + } + + if (chan_id == 0) { + HWREG(config->base + LGPT_O_IMASK) |= 0x100; + HWREG(config->base + LGPT_O_C0CC) = alarm_cfg->ticks; + HWREG(config->base + LGPT_O_C0CFG) = 0x9D; + } else if (chan_id == 1) { + HWREG(config->base + LGPT_O_IMASK) |= 0x200; + HWREG(config->base + LGPT_O_C1CC) = alarm_cfg->ticks; + HWREG(config->base + LGPT_O_C1CFG) = 0x9D; + } else if (chan_id == 2) { + HWREG(config->base + LGPT_O_IMASK) |= 0x400; + HWREG(config->base + LGPT_O_C2CC) = alarm_cfg->ticks; + HWREG(config->base + LGPT_O_C2CFG) = 0x9D; + } else { + LOG_ERR("Invalid chan ID\n"); + return -ENOTSUP; + } + + data->alarm_cfg[chan_id].flags = 0; + data->alarm_cfg[chan_id].ticks = alarm_cfg->ticks; + data->alarm_cfg[chan_id].callback = alarm_cfg->callback; + data->alarm_cfg[chan_id].user_data = alarm_cfg->user_data; + + return 0; +} + +static int counter_cc23x0_lgpt_cancel_alarm(const struct device *dev, uint8_t chan_id) +{ + const struct counter_cc23x0_lgpt_config *config = dev->config; + struct counter_cc23x0_lgpt_data *data = dev->data; + + if (chan_id == 0) { + HWREG(config->base + LGPT_O_IMCLR) |= 0x100; + HWREG(config->base + LGPT_O_C0CC) = 0x0; + HWREG(config->base + LGPT_O_C0CFG) = 0x0; + } else if (chan_id == 1) { + HWREG(config->base + LGPT_O_IMCLR) |= 0x200; + HWREG(config->base + LGPT_O_C1CC) = 0; + HWREG(config->base + LGPT_O_C1CFG) = 0; + } else if (chan_id == 2) { + HWREG(config->base + LGPT_O_IMCLR) |= 0x400; + HWREG(config->base + LGPT_O_C2CC) = 0; + HWREG(config->base + LGPT_O_C2CFG) = 0; + } else { + LOG_ERR("Invalid chan ID\n"); + return -ENOTSUP; + } + + data->alarm_cfg[chan_id].flags = 0; + data->alarm_cfg[chan_id].ticks = 0; + data->alarm_cfg[chan_id].callback = NULL; + data->alarm_cfg[chan_id].user_data = NULL; + + return 0; +} + +static uint32_t counter_cc23x0_lgpt_get_top_value(const struct device *dev) +{ + const struct counter_cc23x0_lgpt_config *config = dev->config; + + return HWREG(config->base + LGPT_O_TGT); +} + +static int counter_cc23x0_lgpt_set_top_value(const struct device *dev, + const struct counter_top_cfg *cfg) +{ + const struct counter_cc23x0_lgpt_config *config = dev->config; + struct counter_cc23x0_lgpt_data *data = dev->data; + int ret = 0; + + /* If not running set new top value */ + if (HWREG(config->base + LGPT_O_STARTCFG) == 0) { + HWREG(config->base + LGPT_O_TGT) = config->counter_info.max_top_value; + + HWREG(config->base + LGPT_O_IMASK) |= 0x001; + HWREG(config->base + LGPT_O_TGT) = cfg->ticks; + + data->target_cfg.flags = 0; + data->target_cfg.ticks = cfg->ticks; + data->target_cfg.callback = cfg->callback; + data->target_cfg.user_data = cfg->user_data; + } else { + ret = -EBUSY; + } + + return ret; +} + +static uint32_t counter_cc23x0_lgpt_get_pending_int(const struct device *dev) +{ + const struct counter_cc23x0_lgpt_config *config = dev->config; + + return HWREG(config->base + LGPT_O_RIS) & HWREG(config->base + LGPT_O_MIS) ? 1 : 0; +} + +static int counter_cc23x0_lgpt_start(const struct device *dev) +{ + const struct counter_cc23x0_lgpt_config *config = dev->config; + + LOG_DBG("[START] LGPT base[%x]\n", config->base); + + HWREG(config->base + LGPT_O_CTL) = LGPT_CTL_MODE_UP_PER; + + /* Set to 1 to start timer */ + HWREG(config->base + LGPT_O_STARTCFG) = 0x1; + + return 0; +} + +static int counter_cc23x0_lgpt_stop(const struct device *dev) +{ + const struct counter_cc23x0_lgpt_config *config = dev->config; + + LOG_DBG("[STOP] LGPT base[%x]\n", config->base); + + /* Set to 0 to stop timer */ + HWREG(config->base + LGPT_O_STARTCFG) = 0x0; + + return 0; +} + +static const struct counter_driver_api cc23x0_lgpt_api = { + .start = counter_cc23x0_lgpt_start, + .stop = counter_cc23x0_lgpt_stop, + .get_value = counter_cc23x0_lgpt_get_value, + .set_alarm = counter_cc23x0_lgpt_set_alarm, + .cancel_alarm = counter_cc23x0_lgpt_cancel_alarm, + .get_top_value = counter_cc23x0_lgpt_get_top_value, + .set_top_value = counter_cc23x0_lgpt_set_top_value, + .get_pending_int = counter_cc23x0_lgpt_get_pending_int, + .get_freq = counter_cc23x0_lgpt_get_freq, +}; + +#define LGPT_CLK_PRESCALE(pres) ((pres + 1) << 8) + +#define LGPT_CC23X0_INIT_FUNC(inst) \ + static int counter_cc23x0_lgpt_init##inst(const struct device *dev) \ + { \ + const struct counter_cc23x0_lgpt_config *config = dev->config; \ + \ + CLKCTLEnable(CLKCTL_BASE, CLKCTL_LGPT##inst); \ + \ + IRQ_CONNECT(DT_INST_IRQN(inst), DT_INST_IRQ(inst, priority), \ + counter_cc23x0_lgpt_isr, DEVICE_DT_INST_GET(inst), 0); \ + \ + irq_enable(DT_INST_IRQN(inst)); \ + \ + HWREG(config->base + LGPT_O_TGT) = config->counter_info.max_top_value; \ + \ + HWREG(config->base + LGPT_O_PRECFG) = LGPT_CLK_PRESCALE(config->prescale); \ + \ + HWREG(EVTSVT_BASE + EVTSVT_O_LGPTSYNCSEL) = EVTSVT_LGPTSYNCSEL_PUBID_SYSTIM0; \ + \ + return 0; \ + } + +#define CC23X0_LGPT_INIT(inst) \ + \ + LGPT_CC23X0_INIT_FUNC(inst); \ + \ + static const struct counter_cc23x0_lgpt_config cc23x0_lgpt_config_##inst = { \ + .counter_info = \ + { \ + .max_top_value = DT_INST_PROP(inst, max_top_value), \ + .flags = COUNTER_CONFIG_INFO_COUNT_UP, \ + .channels = 3, \ + }, \ + .base = DT_INST_REG_ADDR(inst), \ + .prescale = DT_INST_PROP(inst, clk_prescale), \ + }; \ + \ + static struct counter_cc23x0_lgpt_data cc23x0_lgpt_data_##inst; \ + \ + DEVICE_DT_INST_DEFINE(inst, &counter_cc23x0_lgpt_init##inst, NULL, \ + &cc23x0_lgpt_data_##inst, &cc23x0_lgpt_config_##inst, POST_KERNEL, \ + CONFIG_COUNTER_INIT_PRIORITY, &cc23x0_lgpt_api); + +DT_INST_FOREACH_STATUS_OKAY(CC23X0_LGPT_INIT); diff --git a/dts/arm/ti/cc23x0.dtsi b/dts/arm/ti/cc23x0.dtsi index 6b307980ce3e..eea11233cbea 100644 --- a/dts/arm/ti/cc23x0.dtsi +++ b/dts/arm/ti/cc23x0.dtsi @@ -79,6 +79,42 @@ clocks = <&sysclk>; status = "disabled"; }; + + lgpt0: lgpt@40060000 { + compatible = "ti,cc23x0-lgpt"; + reg = <0x40060000 0x1d2>; + max-top-value = <0xffff>; + interrupts = <13 0>; + clk-prescale = <0>; + status = "disabled"; + }; + + lgpt1: lgpt@40061000 { + compatible = "ti,cc23x0-lgpt"; + reg = <0x40061000 0x1d2>; + max-top-value = <0xffff>; + interrupts = <14 0>; + clk-prescale = <0>; + status = "disabled"; + }; + + lgpt2: lgpt@40062000 { + compatible = "ti,cc23x0-lgpt"; + reg = <0x40062000 0x1d2>; + max-top-value = <0xffff>; + interrupts = <17 0>; + clk-prescale = <0>; + status = "disabled"; + }; + + lgpt3: lgpt@40063000 { + compatible = "ti,cc23x0-lgpt"; + reg = <0x40063000 0x1d2>; + max-top-value = <0xffffff>; + interrupts = <18 0>; + clk-prescale = <0>; + status = "disabled"; + }; }; }; diff --git a/dts/bindings/counter/ti,cc23x0-lgpt.yaml b/dts/bindings/counter/ti,cc23x0-lgpt.yaml new file mode 100644 index 000000000000..ab809c762718 --- /dev/null +++ b/dts/bindings/counter/ti,cc23x0-lgpt.yaml @@ -0,0 +1,26 @@ +# Copyright (c) Copyright (c) 2024 BayLibre, SAS +# SPDX-License-Identifier: Apache-2.0 + +description: CC23x0 LGPT counter driver + +properties: + freq: + type: int + description: clock frequency (only used for external clock sources) + + interrupts: + required: true + + clk-prescale: + type: int + required: true + description: Counter clock prescale + + max-top-value: + type: int + required: true + description: Max allowed value for counter + +compatible: "ti,cc23x0-lgpt" + +include: base.yaml