| /* |
| * Copyright (c) 2024 BayLibre, SAS |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #define DT_DRV_COMPAT ti_cc23x0_lgpt |
| |
| #include <zephyr/device.h> |
| #include <zephyr/drivers/counter.h> |
| #include <zephyr/spinlock.h> |
| #include <zephyr/kernel.h> |
| #include <zephyr/drivers/clock_control.h> |
| #include <zephyr/logging/log.h> |
| |
| #include <driverlib/clkctl.h> |
| #include <inc/hw_lgpt.h> |
| #include <inc/hw_lgpt1.h> |
| #include <inc/hw_lgpt3.h> |
| #include <inc/hw_types.h> |
| #include <inc/hw_evtsvt.h> |
| #include <inc/hw_memmap.h> |
| |
| 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 DEVICE_API(counter, 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); |