diff --git a/boards/renesas/ek_ra8d1/doc/index.rst b/boards/renesas/ek_ra8d1/doc/index.rst index 6b8f83272349f..bfe7bcd96edf0 100644 --- a/boards/renesas/ek_ra8d1/doc/index.rst +++ b/boards/renesas/ek_ra8d1/doc/index.rst @@ -106,6 +106,8 @@ The below features are currently supported on Zephyr OS for EK-RA8D1 board: +--------------+------------+------------------+ | PWM | on-chip | pwm | +--------------+------------+------------------+ +| COUNTER | on-chip | counter | ++--------------+------------+------------------+ Other hardware features are currently not supported by the port. diff --git a/boards/renesas/ek_ra8m1/doc/index.rst b/boards/renesas/ek_ra8m1/doc/index.rst index 21897fff0bff4..ff8a5b9accb17 100644 --- a/boards/renesas/ek_ra8m1/doc/index.rst +++ b/boards/renesas/ek_ra8m1/doc/index.rst @@ -108,6 +108,8 @@ The below features are currently supported on Zephyr OS for EK-RA8M1 board: +-----------+------------+----------------------+ | PWM | on-chip | pwm | +-----------+------------+----------------------+ +| COUNTER | on-chip | counter | ++-----------+------------+----------------------+ Other hardware features are currently not supported by the port. diff --git a/boards/renesas/mck_ra8t1/doc/index.rst b/boards/renesas/mck_ra8t1/doc/index.rst index a32692eb7b18f..921989ff16a28 100644 --- a/boards/renesas/mck_ra8t1/doc/index.rst +++ b/boards/renesas/mck_ra8t1/doc/index.rst @@ -104,6 +104,8 @@ The below features are currently supported on Zephyr OS for MCB-RA8T1 board: +--------------+------------+----------------------+ | PWM | on-chip | pwm | +--------------+------------+----------------------+ +| COUNTER | on-chip | counter | ++--------------+------------+----------------------+ Other hardware features are currently not supported by the port. diff --git a/drivers/counter/CMakeLists.txt b/drivers/counter/CMakeLists.txt index 0189a77641177..157d55060408f 100644 --- a/drivers/counter/CMakeLists.txt +++ b/drivers/counter/CMakeLists.txt @@ -51,3 +51,4 @@ zephyr_library_sources_ifdef(CONFIG_COUNTER_SHELL counter_timer_sh zephyr_library_sources_ifdef(CONFIG_COUNTER_TIMER_RPI_PICO counter_rpi_pico_timer.c) zephyr_library_sources_ifdef(CONFIG_COUNTER_TIMER_MAX32 counter_max32_timer.c) 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) diff --git a/drivers/counter/Kconfig b/drivers/counter/Kconfig index 952c43aa5ab0c..c801b413d86d7 100644 --- a/drivers/counter/Kconfig +++ b/drivers/counter/Kconfig @@ -100,4 +100,6 @@ source "drivers/counter/Kconfig.max32_timer" source "drivers/counter/Kconfig.nxp_mrt" +source "drivers/counter/Kconfig.renesas_ra" + endif # COUNTER diff --git a/drivers/counter/Kconfig.renesas_ra b/drivers/counter/Kconfig.renesas_ra new file mode 100644 index 0000000000000..6d24026d72dcc --- /dev/null +++ b/drivers/counter/Kconfig.renesas_ra @@ -0,0 +1,11 @@ +# Renesas RA Family + +# Copyright (c) 2024 Renesas Electronics Corporation +# SPDX-License-Identifier: Apache-2.0 + +config COUNTER_RA_AGT + bool "Renesas RA AGT driver" + default y + depends on DT_HAS_RENESAS_RA_AGT_COUNTER_ENABLED + help + Enable support for Renesas Low Power Asynchronous General Purpose Timer (AGT) driver. diff --git a/drivers/counter/counter_renesas_ra_agt.c b/drivers/counter/counter_renesas_ra_agt.c new file mode 100644 index 0000000000000..0294bbed6a30a --- /dev/null +++ b/drivers/counter/counter_renesas_ra_agt.c @@ -0,0 +1,593 @@ +/* + * Copyright (c) 2024 Renesas Electronics Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT renesas_ra_agt_counter + +#include +#include +#include +#include +#include + +#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, \ + .source_div = DT_PROP(TIMER(n), prescaler), \ + .count_source = DT_STRING_TOKEN(TIMER(n), count_source), \ + .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), \ + .resolution = DT_PROP(TIMER(n), resolution), \ + .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) diff --git a/dts/arm/renesas/ra/ra8/ra8x1.dtsi b/dts/arm/renesas/ra/ra8/ra8x1.dtsi index 20c88bb4e571d..c4ba97aba984e 100644 --- a/dts/arm/renesas/ra/ra8/ra8x1.dtsi +++ b/dts/arm/renesas/ra/ra8/ra8x1.dtsi @@ -493,6 +493,40 @@ zephyr,memory-region = "OPTION_SETTING_S"; status = "okay"; }; + + agt0: agt0@40221000 { + compatible = "renesas,ra-agt"; + channel = <0>; + reg = <0x40221000 0x100>; + interrupts = <83 1>, <84 1>; + interrupt-names = "agti", "agtcmai"; + renesas,count-source = "AGT_CLOCK_LOCO"; + renesas,prescaler = <0>; + renesas,resolution = <16>; + status = "disabled"; + + counter { + compatible = "renesas,ra-agt-counter"; + status = "disabled"; + }; + }; + + agt1: agt0@40221100 { + compatible = "renesas,ra-agt"; + channel = <1>; + reg = <0x40221100 0x100>; + interrupts = <85 1>, <86 1>; + interrupt-names = "agti", "agtcmai"; + renesas,count-source = "AGT_CLOCK_LOCO"; + renesas,prescaler = <0>; + renesas,resolution = <16>; + status = "disabled"; + + counter { + compatible = "renesas,ra-agt-counter"; + status = "disabled"; + }; + }; }; }; diff --git a/dts/bindings/counter/renesas,ra-agt-counter.yml b/dts/bindings/counter/renesas,ra-agt-counter.yml new file mode 100644 index 0000000000000..d556962f8f487 --- /dev/null +++ b/dts/bindings/counter/renesas,ra-agt-counter.yml @@ -0,0 +1,8 @@ +# Copyright (c) 2024 Renesas Electronics Corporation +# SPDX-License-Identifier: Apache-2.0 + +description: Renesas RA AGT as Counter. + +compatible: "renesas,ra-agt-counter" + +include: base.yaml diff --git a/dts/bindings/misc/renesas,ra-agt.yaml b/dts/bindings/misc/renesas,ra-agt.yaml new file mode 100644 index 0000000000000..98de1c13c4f32 --- /dev/null +++ b/dts/bindings/misc/renesas,ra-agt.yaml @@ -0,0 +1,51 @@ +# Copyright (c) 2024 Renesas Electronics Corporation +# SPDX-License-Identifier: Apache-2.0 + +description: Renesas RA AGT + +compatible: "renesas,ra-agt" + +include: base.yaml + +properties: + reg: + required: true + + channel: + type: int + required: true + + renesas,count-source: + description: AGT clock source. + type: string + required: true + enum: + - "AGT_CLOCK_PCLKB" + - "AGT_CLOCK_LOCO" + + renesas,prescaler: + description: | + AGT clock divider for LOCO and SUBCLOCK. timer clock = (clock-source / (1 << prescaler)) + For LOCO and SUBCLOCK, prescler could be in range [0 .. 7]. + For PCLKB, prescler could be in 0, 1, and 3. + type: int + required: true + enum: + - 0 + - 1 + - 2 + - 3 + - 4 + - 5 + - 6 + - 7 + + renesas,resolution: + type: int + required: true + + interrupts: + required: true + + interrupt-names: + required: true diff --git a/modules/Kconfig.renesas_fsp b/modules/Kconfig.renesas_fsp index c56dcc2d60e06..661f8bdbf58ef 100644 --- a/modules/Kconfig.renesas_fsp +++ b/modules/Kconfig.renesas_fsp @@ -66,4 +66,9 @@ config USE_RA_FSP_GPT help Enable RA FSP GPT driver +config USE_RA_FSP_AGT + bool + help + Enable RA FSP AGT driver + endif # HAS_RENESAS_RA_FSP diff --git a/samples/drivers/counter/alarm/boards/ek_ra8d1.overlay b/samples/drivers/counter/alarm/boards/ek_ra8d1.overlay new file mode 100644 index 0000000000000..0799970c5b299 --- /dev/null +++ b/samples/drivers/counter/alarm/boards/ek_ra8d1.overlay @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2024 Renesas Electronics Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&agt0 { + status = "okay"; + prescaler = <4>; + counter0: counter { + status = "okay"; + }; +}; diff --git a/samples/drivers/counter/alarm/boards/ek_ra8m1.overlay b/samples/drivers/counter/alarm/boards/ek_ra8m1.overlay new file mode 100644 index 0000000000000..0799970c5b299 --- /dev/null +++ b/samples/drivers/counter/alarm/boards/ek_ra8m1.overlay @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2024 Renesas Electronics Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&agt0 { + status = "okay"; + prescaler = <4>; + counter0: counter { + status = "okay"; + }; +}; diff --git a/samples/drivers/counter/alarm/boards/mck_ra8t1.overlay b/samples/drivers/counter/alarm/boards/mck_ra8t1.overlay new file mode 100644 index 0000000000000..0799970c5b299 --- /dev/null +++ b/samples/drivers/counter/alarm/boards/mck_ra8t1.overlay @@ -0,0 +1,13 @@ +/* + * Copyright (c) 2024 Renesas Electronics Corporation + * + * SPDX-License-Identifier: Apache-2.0 + */ + +&agt0 { + status = "okay"; + prescaler = <4>; + counter0: counter { + status = "okay"; + }; +}; diff --git a/samples/drivers/counter/alarm/src/main.c b/samples/drivers/counter/alarm/src/main.c index b34218d9f3ba3..deb64e21540e1 100644 --- a/samples/drivers/counter/alarm/src/main.c +++ b/samples/drivers/counter/alarm/src/main.c @@ -55,6 +55,8 @@ struct counter_alarm_cfg alarm_cfg; #define TIMER DT_NODELABEL(timer) #elif defined(CONFIG_COUNTER_TIMER_MAX32) #define TIMER DT_NODELABEL(counter0) +#elif defined(CONFIG_COUNTER_RA_AGT) +#define TIMER DT_NODELABEL(counter0) #else #error Unable to find a counter device node in devicetree #endif @@ -70,6 +72,10 @@ static void test_counter_interrupt_fn(const struct device *counter_dev, int err; err = counter_get_value(counter_dev, &now_ticks); + if (!counter_is_counting_up(counter_dev)) { + now_ticks = counter_get_top_value(counter_dev) - now_ticks; + } + if (err) { printk("Failed to read counter value (err %d)", err); return;