2024-06-21 12:24:50 +08:00
|
|
|
/*
|
|
|
|
* Copyright (c) 2024 Renesas Electronics Corporation
|
|
|
|
*
|
|
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
|
|
*/
|
|
|
|
|
|
|
|
#define DT_DRV_COMPAT renesas_ra_agt_counter
|
|
|
|
|
|
|
|
#include <zephyr/devicetree.h>
|
|
|
|
#include <zephyr/drivers/counter.h>
|
|
|
|
#include <zephyr/irq.h>
|
|
|
|
#include <r_agt.h>
|
|
|
|
#include <zephyr/logging/log.h>
|
|
|
|
|
|
|
|
#define FSUB_FREQUENCY_HZ (32768U)
|
|
|
|
typedef volatile R_AGTX0_AGT16_CTRL_Type agt_reg_ctrl_t;
|
|
|
|
#define AGT_AGTCR_STOP_TIMER 0xF0U /* 1111 0000 */
|
|
|
|
#define AGT_AGTCR_START_TIMER 0xF1U /* 1111 0001 */
|
|
|
|
#define AGT_AGTCR_FLAGS_MASK 0xF0U /* 1111 0000 */
|
|
|
|
|
|
|
|
#define AGT_PRV_MIN_CLOCK_FREQ (0U)
|
|
|
|
#define AGT_SOURCE_CLOCK_PCLKB_BITS (0x3U)
|
|
|
|
|
|
|
|
LOG_MODULE_REGISTER(ra_agt_counter, CONFIG_COUNTER_LOG_LEVEL);
|
|
|
|
|
|
|
|
struct counter_ra_agt_config {
|
|
|
|
/* info must be first element */
|
|
|
|
struct counter_config_info info; /* Counter Config info struct */
|
|
|
|
uint32_t channel; /* Channel no */
|
|
|
|
uint32_t cycle_end_irq; /* Underflow interrupt*/
|
|
|
|
uint32_t cycle_end_ipl; /* Underflow interrupt priority */
|
|
|
|
uint32_t channel_ipl; /* IPL channel no. */
|
|
|
|
uint32_t channel_irq; /* IRQ channel no. */
|
|
|
|
/* Device tree data */
|
|
|
|
timer_source_div_t source_div; /* Clock source divider */
|
|
|
|
agt_agtio_filter_t agtio_filter; /* Input filter for AGTIO */
|
|
|
|
agt_measure_t measurement_mode; /* Measurement mode */
|
|
|
|
agt_clock_t count_source; /* AGT channel clock source */
|
|
|
|
uint32_t resolution; /* AGT node resolution */
|
|
|
|
uint32_t dt_reg; /* Reg address from device tree */
|
|
|
|
};
|
|
|
|
|
|
|
|
struct counter_ra_agt_alarm {
|
|
|
|
counter_alarm_callback_t callback;
|
|
|
|
void *data;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct counter_ra_agt_data {
|
|
|
|
/* Common data */
|
|
|
|
R_AGTX0_Type *agt_reg; /* AGT register base address */
|
|
|
|
uint32_t period; /* Current timer period (counts) */
|
|
|
|
uint32_t period_counts; /* Period in raw timer counts */
|
|
|
|
uint32_t cycle_end_ipl; /* Cycle end interrupt priority */
|
|
|
|
IRQn_Type cycle_end_irq; /* Cycle end interrupt */
|
|
|
|
/* Alarm-related data */
|
|
|
|
struct counter_ra_agt_alarm alarm; /* Counter alarm config struct */
|
|
|
|
counter_top_callback_t top_cb; /* Top level callback */
|
|
|
|
void *top_cb_data; /* Top level callback data */
|
|
|
|
uint32_t guard_period; /* Absolute counter alarm's guard period */
|
|
|
|
};
|
|
|
|
|
|
|
|
static void r_agt_hardware_cfg(const struct device *dev);
|
|
|
|
static void r_agt_period_register_set(const struct device *dev, uint32_t period_counts);
|
|
|
|
static uint32_t r_agt_clock_frequency_get(agt_reg_ctrl_t *p_reg);
|
|
|
|
static fsp_err_t r_agt_common_preamble(agt_reg_ctrl_t *p_reg);
|
|
|
|
static agt_reg_ctrl_t *r_agt_reg_ctrl_get(const struct device *dev);
|
|
|
|
static uint32_t r_agt_ticks_sub(uint32_t val, uint32_t old, uint32_t top);
|
|
|
|
|
|
|
|
static int counter_ra_agt_start(const struct device *dev)
|
|
|
|
{
|
|
|
|
agt_reg_ctrl_t *const reg = r_agt_reg_ctrl_get(dev);
|
|
|
|
uint32_t timeout = UINT32_MAX;
|
|
|
|
|
|
|
|
reg->AGTCR = AGT_AGTCR_START_TIMER;
|
|
|
|
|
|
|
|
while (!(reg->AGTCR & BIT(R_AGTX0_AGT16_CTRL_AGTCR_TCSTF_Pos)) && likely(--timeout))
|
|
|
|
;
|
|
|
|
|
|
|
|
return timeout > 0 ? 0 : -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int counter_ra_agt_stop(const struct device *dev)
|
|
|
|
{
|
|
|
|
agt_reg_ctrl_t *const reg = r_agt_reg_ctrl_get(dev);
|
|
|
|
uint32_t timeout = UINT32_MAX;
|
|
|
|
|
|
|
|
reg->AGTCR = AGT_AGTCR_STOP_TIMER;
|
|
|
|
|
|
|
|
while ((reg->AGTCR & BIT(R_AGTX0_AGT16_CTRL_AGTCR_TCSTF_Pos)) && likely(--timeout))
|
|
|
|
;
|
|
|
|
|
|
|
|
return timeout > 0 ? 0 : -EIO;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline int counter_ra_agt_read(const struct device *dev)
|
|
|
|
{
|
|
|
|
struct counter_ra_agt_data *data = dev->data;
|
|
|
|
|
|
|
|
return data->agt_reg->AGT16.AGT;
|
|
|
|
}
|
|
|
|
static int counter_ra_agt_get_value(const struct device *dev, uint32_t *ticks)
|
|
|
|
{
|
|
|
|
*ticks = counter_ra_agt_read(dev);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint32_t counter_ra_agt_get_top_value(const struct device *dev)
|
|
|
|
{
|
|
|
|
const struct counter_ra_agt_config *config = dev->config;
|
|
|
|
|
|
|
|
return config->info.max_top_value;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int counter_ra_agt_set_top_value(const struct device *dev, const struct counter_top_cfg *cfg)
|
|
|
|
{
|
|
|
|
const struct counter_ra_agt_config *config = dev->config;
|
|
|
|
struct counter_ra_agt_data *data = dev->data;
|
|
|
|
agt_reg_ctrl_t *p_reg_ctrl = r_agt_reg_ctrl_get(dev);
|
|
|
|
|
|
|
|
if (cfg->ticks != config->info.max_top_value) {
|
|
|
|
return -ENOTSUP;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (cfg->callback == NULL) {
|
|
|
|
/* Disable Match Register A if callback is NULL */
|
|
|
|
p_reg_ctrl->AGTCR_b.TCMAF = 0;
|
|
|
|
} else {
|
|
|
|
/* Enable Match Register A */
|
|
|
|
p_reg_ctrl->AGTCR_b.TCMAF = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
data->top_cb = cfg->callback;
|
|
|
|
data->top_cb_data = cfg->user_data;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void counter_ra_agt_set_compare_value(const struct device *dev, uint8_t chan,
|
|
|
|
uint32_t value)
|
|
|
|
{
|
|
|
|
struct counter_ra_agt_data *data = dev->data;
|
|
|
|
|
|
|
|
data->agt_reg->AGT16.AGTCMA = value;
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void counter_ra_agt_enable_channel_irq(const struct device *dev)
|
|
|
|
{
|
|
|
|
const struct counter_ra_agt_config *config = dev->config;
|
|
|
|
agt_reg_ctrl_t *p_reg_ctrl = r_agt_reg_ctrl_get(dev);
|
|
|
|
|
|
|
|
/* Enable AGT compare match A */
|
|
|
|
p_reg_ctrl->AGTCMSR |= BIT(R_AGTX0_AGT16_CTRL_AGTCMSR_TCMEA_Pos);
|
|
|
|
irq_enable(config->channel_irq);
|
|
|
|
}
|
|
|
|
|
|
|
|
static inline void counter_ra_agt_clear_channel_irq(const struct device *dev)
|
|
|
|
{
|
|
|
|
const struct counter_ra_agt_config *config = dev->config;
|
|
|
|
agt_reg_ctrl_t *const reg_ctrl = r_agt_reg_ctrl_get(dev);
|
|
|
|
|
|
|
|
reg_ctrl->AGTCR_b.TCMAF = 0;
|
|
|
|
R_BSP_IrqStatusClear(config->channel_irq);
|
|
|
|
NVIC_ClearPendingIRQ(config->channel_irq);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int counter_ra_agt_set_alarm(const struct device *dev, uint8_t chan,
|
|
|
|
const struct counter_alarm_cfg *alarm_cfg)
|
|
|
|
{
|
|
|
|
struct counter_ra_agt_data *data = dev->data;
|
|
|
|
const struct counter_ra_agt_config *config = dev->config;
|
|
|
|
|
|
|
|
const bool absolute = alarm_cfg->flags & COUNTER_ALARM_CFG_ABSOLUTE;
|
|
|
|
const uint32_t top = counter_ra_agt_get_top_value(dev);
|
|
|
|
struct counter_ra_agt_alarm *const alarm = &data->alarm;
|
|
|
|
uint16_t val = alarm_cfg->ticks;
|
|
|
|
uint32_t now;
|
|
|
|
uint32_t max_rel_val;
|
|
|
|
bool irq_on_late;
|
|
|
|
int32_t diff = 0;
|
|
|
|
int err = 0;
|
|
|
|
|
|
|
|
if (alarm_cfg->ticks > counter_ra_agt_get_top_value(dev)) {
|
|
|
|
LOG_ERR("%s: alarm ticks is larger than top value", __func__);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (alarm->callback) {
|
|
|
|
LOG_ERR("%s: Device busy. Callback is still available.", __func__);
|
|
|
|
return -EBUSY;
|
|
|
|
}
|
|
|
|
alarm->callback = alarm_cfg->callback;
|
|
|
|
alarm->data = alarm_cfg->user_data;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* stop AGT for writing the match register directly instead of sync when underflow event
|
|
|
|
* occur reference: RA6M5 User manual - 22.3.2 Reload Register and AGT Compare Match A/B
|
|
|
|
* Register Rewrite Operation
|
|
|
|
*/
|
|
|
|
counter_ra_agt_stop(dev);
|
|
|
|
now = counter_ra_agt_read(dev);
|
|
|
|
|
|
|
|
if (absolute) {
|
|
|
|
/* Acceptable alarm value range, counting from current tick (now) */
|
|
|
|
max_rel_val = top - data->guard_period;
|
|
|
|
irq_on_late = alarm_cfg->flags & COUNTER_ALARM_CFG_EXPIRE_WHEN_LATE;
|
|
|
|
} else {
|
|
|
|
/* If relative value is smaller than half of the counter range
|
|
|
|
* it is assumed that there is a risk of setting value too late
|
|
|
|
* and late detection algorithm must be applied. When late
|
|
|
|
* setting is detected, interrupt shall be triggered for
|
|
|
|
* immediate expiration of the timer. Detection is performed
|
|
|
|
* by limiting relative distance between CC and counter.
|
|
|
|
*
|
|
|
|
* Note that half of counter range is an arbitrary value.
|
|
|
|
*/
|
|
|
|
irq_on_late = (val < (top / 2U));
|
|
|
|
/* Limit max to detect short relative being set too late */
|
|
|
|
max_rel_val = irq_on_late ? top / 2U : top;
|
|
|
|
/* Recalculate alarm tick timestamp based on current tick (now) */
|
|
|
|
val = r_agt_ticks_sub(now, val, top);
|
|
|
|
}
|
|
|
|
|
|
|
|
counter_ra_agt_set_compare_value(dev, chan, val);
|
|
|
|
|
|
|
|
/* Decrement value to detect also case when val == counter_ra_agt_read(dev).
|
|
|
|
* Otherwise, condition would need to include comparing diff against 0.
|
|
|
|
*/
|
|
|
|
diff = r_agt_ticks_sub(now, val - 1, top);
|
|
|
|
|
|
|
|
if (diff > max_rel_val) {
|
|
|
|
if (absolute) {
|
|
|
|
err = -ETIME;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Interrupt is triggered always for relative alarm and
|
|
|
|
* for absolute depending on the flag.
|
|
|
|
*/
|
|
|
|
if (irq_on_late) {
|
|
|
|
/* Set pending IRQ */
|
|
|
|
counter_ra_agt_enable_channel_irq(dev);
|
|
|
|
NVIC_SetPendingIRQ(config->channel_irq);
|
|
|
|
} else {
|
|
|
|
alarm->callback = NULL;
|
|
|
|
alarm->data = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
counter_ra_agt_clear_channel_irq(dev);
|
|
|
|
counter_ra_agt_enable_channel_irq(dev);
|
|
|
|
counter_ra_agt_start(dev);
|
|
|
|
|
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int counter_ra_agt_cancel_alarm(const struct device *dev, uint8_t chan)
|
|
|
|
{
|
|
|
|
struct counter_ra_agt_data *data = dev->data;
|
|
|
|
|
|
|
|
agt_reg_ctrl_t *p_reg_ctrl = r_agt_reg_ctrl_get(dev);
|
|
|
|
|
|
|
|
/* Disable AGT compare match A */
|
|
|
|
p_reg_ctrl->AGTCMSR &= ~BIT(R_AGTX0_AGT16_CTRL_AGTCMSR_TCMEA_Pos);
|
|
|
|
|
|
|
|
data->alarm.callback = NULL;
|
|
|
|
data->alarm.data = NULL;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint32_t counter_ra_agt_get_pending_int(const struct device *dev)
|
|
|
|
{
|
|
|
|
agt_reg_ctrl_t *const reg = r_agt_reg_ctrl_get(dev);
|
|
|
|
|
|
|
|
return (reg->AGTCR & AGT_AGTCR_FLAGS_MASK) != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint32_t counter_ra_agt_get_freq(const struct device *dev)
|
|
|
|
{
|
|
|
|
struct counter_ra_agt_data *data = dev->data;
|
|
|
|
timer_info_t info;
|
|
|
|
|
|
|
|
agt_reg_ctrl_t *p_reg = r_agt_reg_ctrl_get(dev);
|
|
|
|
|
|
|
|
r_agt_common_preamble(p_reg);
|
|
|
|
|
|
|
|
/* Get and store period */
|
|
|
|
info.period_counts = data->period;
|
|
|
|
info.clock_frequency = r_agt_clock_frequency_get(p_reg);
|
|
|
|
|
|
|
|
/* AGT supports only counting down direction */
|
|
|
|
info.count_direction = TIMER_DIRECTION_DOWN;
|
|
|
|
|
|
|
|
return info.clock_frequency;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void counter_ra_agt_agti_isr(const struct device *dev)
|
|
|
|
{
|
|
|
|
const struct counter_ra_agt_config *config = dev->config;
|
|
|
|
struct counter_ra_agt_data *data = dev->data;
|
|
|
|
agt_reg_ctrl_t *const reg_ctrl = r_agt_reg_ctrl_get(dev);
|
|
|
|
|
|
|
|
R_BSP_IrqStatusClear(config->cycle_end_irq);
|
|
|
|
|
|
|
|
const uint32_t agtcr = reg_ctrl->AGTCR;
|
|
|
|
|
|
|
|
if (agtcr & BIT(R_AGTX0_AGT16_CTRL_AGTCR_TUNDF_Pos)) {
|
|
|
|
if (data->top_cb) {
|
|
|
|
data->top_cb(dev, data->top_cb_data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
reg_ctrl->AGTCR = (uint8_t)(agtcr & ~(BIT(R_AGTX0_AGT16_CTRL_AGTCR_TUNDF_Pos) |
|
|
|
|
BIT(R_AGTX0_AGT16_CTRL_AGTCR_TEDGF_Pos)));
|
|
|
|
}
|
|
|
|
|
|
|
|
static void counter_ra_agt_agtcmai_isr(const struct device *dev)
|
|
|
|
{
|
|
|
|
const struct counter_ra_agt_config *config = dev->config;
|
|
|
|
struct counter_ra_agt_data *data = dev->data;
|
|
|
|
agt_reg_ctrl_t *const reg_ctrl = r_agt_reg_ctrl_get(dev);
|
|
|
|
struct counter_ra_agt_alarm *const alarm = &data->alarm;
|
|
|
|
|
|
|
|
const uint32_t val = data->agt_reg->AGT16.AGTCMA;
|
|
|
|
|
|
|
|
const counter_alarm_callback_t cb = alarm->callback;
|
|
|
|
void *cb_data = alarm->data;
|
|
|
|
|
|
|
|
alarm->callback = NULL;
|
|
|
|
alarm->data = NULL;
|
|
|
|
|
|
|
|
/* Disable AGT compare match A */
|
|
|
|
reg_ctrl->AGTCMSR &= ~BIT(R_AGTX0_AGT16_CTRL_AGTCMSR_TCMEA_Pos);
|
|
|
|
|
|
|
|
R_BSP_IrqStatusClear(config->channel_irq);
|
|
|
|
|
|
|
|
if (cb) {
|
|
|
|
cb(dev, config->channel, val, cb_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
reg_ctrl->AGTCR_b.TCMAF = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint32_t counter_ra_agt_get_guard_period(const struct device *dev, uint32_t flags)
|
|
|
|
{
|
|
|
|
struct counter_ra_agt_data *data = dev->data;
|
|
|
|
|
|
|
|
return data->guard_period;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int counter_ra_agt_set_guard_period(const struct device *dev, uint32_t guard, uint32_t flags)
|
|
|
|
{
|
|
|
|
struct counter_ra_agt_data *data = dev->data;
|
|
|
|
|
|
|
|
if (counter_ra_agt_get_top_value(dev) < guard) {
|
|
|
|
LOG_ERR("%s: Invalid guard rate", __func__);
|
|
|
|
return -EINVAL;
|
|
|
|
}
|
|
|
|
|
|
|
|
data->guard_period = guard;
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int counter_ra_agt_init(const struct device *dev)
|
|
|
|
{
|
|
|
|
const struct counter_ra_agt_config *config = dev->config;
|
|
|
|
struct counter_ra_agt_data *data = dev->data;
|
|
|
|
|
|
|
|
data->agt_reg = (R_AGTX0_Type *)config->dt_reg;
|
|
|
|
|
|
|
|
agt_reg_ctrl_t *p_reg_ctrl = r_agt_reg_ctrl_get(dev);
|
|
|
|
|
|
|
|
/* Power on the AGT channel */
|
|
|
|
R_BSP_MODULE_START(FSP_IP_AGT, config->channel);
|
|
|
|
|
|
|
|
/* Clear AGTCR. This stops the timer if it is running and clears the flags */
|
|
|
|
p_reg_ctrl->AGTCR = 0U;
|
|
|
|
|
|
|
|
/* The timer is stopped in sync with the count clock, or in sync with PCLK in event and
|
|
|
|
* external count modes.
|
|
|
|
*/
|
|
|
|
FSP_HARDWARE_REGISTER_WAIT(0U, p_reg_ctrl->AGTCR_b.TCSTF);
|
|
|
|
|
|
|
|
/* Clear AGTMR2 before AGTMR1 is set. Reference Note 3 in section 25.2.6 "AGT Mode Register
|
|
|
|
* 2 (AGTMR2)" of the RA6M3 manual R01UH0886EJ0100.
|
|
|
|
*/
|
|
|
|
p_reg_ctrl->AGTMR2 = 0U;
|
|
|
|
|
|
|
|
/* Set count source and divider and configure pins */
|
|
|
|
r_agt_hardware_cfg(dev);
|
|
|
|
|
|
|
|
/* Set period register and update duty cycle if output mode is used for one-shot or periodic
|
|
|
|
* mode.
|
|
|
|
*/
|
|
|
|
r_agt_period_register_set(dev, data->period_counts);
|
|
|
|
|
|
|
|
/* 22.3.1 Reload Register and Counter Rewrite Operation */
|
|
|
|
p_reg_ctrl->AGTCMSR |= BIT(R_AGTX0_AGT16_CTRL_AGTCMSR_TCMEA_Pos);
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
static agt_reg_ctrl_t *r_agt_reg_ctrl_get(const struct device *dev)
|
|
|
|
{
|
|
|
|
struct counter_ra_agt_data *data = dev->data;
|
|
|
|
agt_reg_ctrl_t *p_reg_ctrl = &data->agt_reg->AGT16.CTRL;
|
|
|
|
|
|
|
|
return p_reg_ctrl;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void r_agt_hardware_cfg(const struct device *dev)
|
|
|
|
{
|
|
|
|
const struct counter_ra_agt_config *config = dev->config;
|
|
|
|
|
|
|
|
/* Update the divider for PCLKB */
|
|
|
|
agt_reg_ctrl_t *p_reg_ctrl = r_agt_reg_ctrl_get(dev);
|
|
|
|
uint32_t count_source_int = (uint32_t)config->count_source;
|
|
|
|
uint32_t agtmr2 = 0U;
|
|
|
|
uint32_t agtcmsr = 0U;
|
|
|
|
uint32_t tedgsel = 0U;
|
|
|
|
uint32_t agtioc = config->agtio_filter;
|
|
|
|
uint32_t mode = config->measurement_mode & R_AGTX0_AGT16_CTRL_AGTMR1_TMOD_Msk;
|
|
|
|
uint32_t edge = 0U;
|
|
|
|
|
|
|
|
if (AGT_CLOCK_PCLKB == config->count_source) {
|
|
|
|
if (TIMER_SOURCE_DIV_1 != config->source_div) {
|
|
|
|
/* Toggle the second bit if the count_source_int is not 0 to map PCLKB / 8
|
|
|
|
* to 1 and PCLKB / 2 to 3.
|
|
|
|
*/
|
|
|
|
count_source_int = config->source_div ^ 2U;
|
|
|
|
count_source_int <<= R_AGTX0_AGT16_CTRL_AGTMR1_TCK_Pos;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
else if (AGT_CLOCK_AGT_UNDERFLOW != config->count_source) {
|
|
|
|
/* Update the divider for LOCO/subclock */
|
|
|
|
agtmr2 = config->source_div;
|
|
|
|
} else {
|
|
|
|
/* No divider can be used when count source is AGT_CLOCK_AGT_UNDERFLOW */
|
|
|
|
}
|
|
|
|
|
|
|
|
uint32_t agtmr1 = (count_source_int | edge) | mode;
|
|
|
|
|
|
|
|
/* Configure output settings */
|
|
|
|
|
|
|
|
agtioc |= tedgsel;
|
|
|
|
|
|
|
|
p_reg_ctrl->AGTIOC = (uint8_t)agtioc;
|
|
|
|
p_reg_ctrl->AGTCMSR = (uint8_t)agtcmsr;
|
|
|
|
p_reg_ctrl->AGTMR1 = (uint8_t)agtmr1;
|
|
|
|
p_reg_ctrl->AGTMR2 = (uint8_t)agtmr2;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void r_agt_period_register_set(const struct device *dev, uint32_t period_counts)
|
|
|
|
{
|
|
|
|
struct counter_ra_agt_data *data = dev->data;
|
|
|
|
|
|
|
|
/* Store the period value so it can be retrieved later */
|
|
|
|
data->period = period_counts;
|
|
|
|
|
|
|
|
/* Set counter to period minus one */
|
|
|
|
uint32_t period_reg = (period_counts - 1U);
|
|
|
|
|
|
|
|
data->agt_reg->AGT16.AGT = (uint16_t)period_reg;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint32_t r_agt_clock_frequency_get(agt_reg_ctrl_t *p_reg)
|
|
|
|
{
|
|
|
|
uint32_t clock_freq_hz = 0U;
|
|
|
|
uint8_t count_source_int = p_reg->AGTMR1_b.TCK;
|
|
|
|
timer_source_div_t divider = TIMER_SOURCE_DIV_1;
|
|
|
|
|
|
|
|
if (0U == (count_source_int & (~AGT_SOURCE_CLOCK_PCLKB_BITS))) {
|
|
|
|
/* Call CGC function to obtain current PCLKB clock frequency */
|
|
|
|
clock_freq_hz = R_FSP_SystemClockHzGet(FSP_PRIV_CLOCK_PCLKB);
|
|
|
|
|
|
|
|
/* If Clock source is PCLKB or derived from PCLKB */
|
|
|
|
divider = (timer_source_div_t)count_source_int;
|
|
|
|
|
|
|
|
if (divider != 0U) {
|
|
|
|
/* Set divider to 3 to divide by 8 when AGTMR1.TCK is 1 (PCLKB / 8). Set
|
|
|
|
* divider to 1 to divide by 2 when AGTMR1.TCK is 3 (PCLKB / 2). XOR with 2
|
|
|
|
* to convert 1 to 3 and 3 to 1.
|
|
|
|
*/
|
|
|
|
divider ^= 2U;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
/* Else either fSUB clock or LOCO clock is used. The frequency is set to 32Khz
|
|
|
|
* (32768). This function does not support AGT0 underflow as count source.
|
|
|
|
*/
|
|
|
|
clock_freq_hz = FSUB_FREQUENCY_HZ;
|
|
|
|
divider = (timer_source_div_t)p_reg->AGTMR2_b.CKS;
|
|
|
|
}
|
|
|
|
|
|
|
|
clock_freq_hz >>= divider;
|
|
|
|
|
|
|
|
return clock_freq_hz;
|
|
|
|
}
|
|
|
|
|
|
|
|
static fsp_err_t r_agt_common_preamble(agt_reg_ctrl_t *p_reg)
|
|
|
|
{
|
|
|
|
/* Ensure timer state reflects expected status. Reference section 25.4.1 "Count Operation
|
|
|
|
* Start and Stop Control" in the RA6M3 manual R01UH0886EJ0100.
|
|
|
|
*/
|
|
|
|
uint32_t agtcr_tstart = p_reg->AGTCR_b.TSTART;
|
|
|
|
|
|
|
|
FSP_HARDWARE_REGISTER_WAIT(agtcr_tstart, p_reg->AGTCR_b.TCSTF);
|
|
|
|
|
|
|
|
return FSP_SUCCESS;
|
|
|
|
}
|
|
|
|
|
|
|
|
static uint32_t r_agt_ticks_sub(uint32_t val, uint32_t old, uint32_t top)
|
|
|
|
{
|
|
|
|
if (likely(IS_BIT_MASK(top))) {
|
|
|
|
return (val - old) & top;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* if top is not 2^n-1 */
|
|
|
|
return (val >= old) ? (val - old) : val + top + 1 - old;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const struct counter_driver_api ra_agt_driver_api = {
|
|
|
|
.start = counter_ra_agt_start,
|
|
|
|
.stop = counter_ra_agt_stop,
|
|
|
|
.get_value = counter_ra_agt_get_value,
|
|
|
|
.set_alarm = counter_ra_agt_set_alarm,
|
|
|
|
.cancel_alarm = counter_ra_agt_cancel_alarm,
|
|
|
|
.set_top_value = counter_ra_agt_set_top_value,
|
|
|
|
.get_pending_int = counter_ra_agt_get_pending_int,
|
|
|
|
.get_top_value = counter_ra_agt_get_top_value,
|
|
|
|
.get_freq = counter_ra_agt_get_freq,
|
|
|
|
.get_guard_period = counter_ra_agt_get_guard_period,
|
|
|
|
.set_guard_period = counter_ra_agt_set_guard_period,
|
|
|
|
};
|
|
|
|
|
|
|
|
#define TIMER(idx) DT_INST_PARENT(idx)
|
|
|
|
|
|
|
|
#define _ELC_EVENT_AGT_INT(channel) ELC_EVENT_AGT##channel##_INT
|
|
|
|
#define _ELC_EVENT_AGT_COMPARE_A(channel) ELC_EVENT_AGT##channel##_COMPARE_A
|
|
|
|
|
|
|
|
#define ELC_EVENT_AGT_INT(channel) _ELC_EVENT_AGT_INT(channel)
|
|
|
|
#define ELC_EVENT_AGT_COMPARE_A(channel) _ELC_EVENT_AGT_COMPARE_A(channel)
|
|
|
|
|
|
|
|
#define AGT_DEVICE_INIT_RA(n) \
|
|
|
|
static const struct counter_ra_agt_config ra_agt_config_##n = { \
|
|
|
|
.info = \
|
|
|
|
{ \
|
|
|
|
.max_top_value = UINT16_MAX, \
|
|
|
|
.freq = 0, \
|
|
|
|
.channels = 1, \
|
|
|
|
.flags = 0, \
|
|
|
|
}, \
|
|
|
|
.agtio_filter = AGT_AGTIO_FILTER_NONE, \
|
|
|
|
.measurement_mode = 0U, \
|
2024-10-22 14:44:22 +08:00
|
|
|
.source_div = DT_PROP(TIMER(n), renesas_prescaler), \
|
|
|
|
.count_source = DT_STRING_TOKEN(TIMER(n), renesas_count_source), \
|
2024-06-21 12:24:50 +08:00
|
|
|
.channel = DT_PROP(TIMER(n), channel), \
|
|
|
|
.channel_irq = DT_IRQ_BY_NAME(TIMER(n), agtcmai, irq), \
|
|
|
|
.channel_ipl = DT_IRQ_BY_NAME(TIMER(n), agtcmai, priority), \
|
|
|
|
.cycle_end_irq = DT_IRQ_BY_NAME(TIMER(n), agti, irq), \
|
|
|
|
.cycle_end_ipl = DT_IRQ_BY_NAME(TIMER(n), agti, priority), \
|
2024-10-22 14:44:22 +08:00
|
|
|
.resolution = DT_PROP(TIMER(n), renesas_resolution), \
|
2024-06-21 12:24:50 +08:00
|
|
|
.dt_reg = DT_REG_ADDR(TIMER(n)), \
|
|
|
|
}; \
|
|
|
|
\
|
|
|
|
static struct counter_ra_agt_data counter_ra_agt_data_##n = { \
|
|
|
|
.period_counts = 0, \
|
|
|
|
}; \
|
|
|
|
\
|
|
|
|
static int counter_ra_agt_##n##_init(const struct device *dev) \
|
|
|
|
{ \
|
|
|
|
R_ICU->IELSR[DT_IRQ_BY_NAME(TIMER(n), agti, irq)] = \
|
|
|
|
ELC_EVENT_AGT_INT(DT_PROP(TIMER(n), channel)); \
|
|
|
|
IRQ_CONNECT(DT_IRQ_BY_NAME(TIMER(n), agti, irq), \
|
|
|
|
DT_IRQ_BY_NAME(TIMER(n), agti, priority), counter_ra_agt_agti_isr, \
|
|
|
|
DEVICE_DT_INST_GET(n), 0); \
|
|
|
|
irq_enable(DT_IRQ_BY_NAME(TIMER(n), agti, irq)); \
|
|
|
|
\
|
|
|
|
R_ICU->IELSR[DT_IRQ_BY_NAME(TIMER(n), agtcmai, irq)] = \
|
|
|
|
ELC_EVENT_AGT_COMPARE_A(DT_PROP(TIMER(n), channel)); \
|
|
|
|
IRQ_CONNECT(DT_IRQ_BY_NAME(TIMER(n), agtcmai, irq), \
|
|
|
|
DT_IRQ_BY_NAME(TIMER(n), agtcmai, priority), \
|
|
|
|
counter_ra_agt_agtcmai_isr, DEVICE_DT_INST_GET(n), 0); \
|
|
|
|
irq_disable(DT_IRQ_BY_NAME(TIMER(n), agtcmai, irq)); \
|
|
|
|
\
|
|
|
|
return counter_ra_agt_init(dev); \
|
|
|
|
} \
|
|
|
|
\
|
|
|
|
DEVICE_DT_INST_DEFINE(n, counter_ra_agt_##n##_init, NULL, &counter_ra_agt_data_##n, \
|
|
|
|
&ra_agt_config_##n, POST_KERNEL, CONFIG_COUNTER_INIT_PRIORITY, \
|
|
|
|
&ra_agt_driver_api);
|
|
|
|
|
|
|
|
DT_INST_FOREACH_STATUS_OKAY(AGT_DEVICE_INIT_RA)
|