Browse Source
Add support for cc23x0 RTC driver in counter. RTC is always ON after device boot. Timer is restared only on POR, and is active during STANDBY and ACTIVE power states. Signed-off-by: Stoyan Bogdanov <sbogdanov@baylibre.com>pull/84529/head
5 changed files with 268 additions and 0 deletions
@ -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 |
@ -0,0 +1,241 @@ |
|||||||
|
/*
|
||||||
|
* Copyright (c) 2024 BayLibre, SAS |
||||||
|
* |
||||||
|
* SPDX-License-Identifier: Apache-2.0 |
||||||
|
*/ |
||||||
|
|
||||||
|
#define DT_DRV_COMPAT ti_cc23x0_rtc |
||||||
|
|
||||||
|
#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 <inc/hw_rtc.h> |
||||||
|
#include <inc/hw_types.h> |
||||||
|
#include <inc/hw_evtsvt.h> |
||||||
|
#include <inc/hw_memmap.h> |
||||||
|
|
||||||
|
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) |
@ -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 |
Loading…
Reference in new issue