From f8267fd4d6d971ca7af997d33eefcdee4756c3ae Mon Sep 17 00:00:00 2001 From: Thao Luong Date: Fri, 21 Jun 2024 11:24:50 +0700 Subject: [PATCH] drivers: counter: Add AGT counter driver support for Renesas RA8 - boards: renesas: Add support for agt. - drivers: counter: Add support for counter driver use agt - dts: arm: Add support for agt. - dts: bindings: Add support for agt counter driver. - soc: renesas: Add support for agt counter driver. - samples: drivers: counter: alarm: Add support for RA8 This is initial support with only basic functionality for counter operation on Zephyr using AGT hardware, current support for count source is limited to LOCO and PCLKB, other count source like underflow signal external pin or AGTIO from another AGT channel will be added in later support Signed-off-by: Ha Nguyen Signed-off-by: Thao Luong Signed-off-by: Duy Phuong Hoang. Nguyen Signed-off-by: Khoa Nguyen --- boards/renesas/ek_ra8d1/doc/index.rst | 2 + boards/renesas/ek_ra8m1/doc/index.rst | 2 + boards/renesas/mck_ra8t1/doc/index.rst | 2 + drivers/counter/CMakeLists.txt | 1 + drivers/counter/Kconfig | 2 + drivers/counter/Kconfig.renesas_ra | 11 + drivers/counter/counter_renesas_ra_agt.c | 593 ++++++++++++++++++ dts/arm/renesas/ra/ra8/ra8x1.dtsi | 34 + .../counter/renesas,ra-agt-counter.yml | 8 + dts/bindings/misc/renesas,ra-agt.yaml | 51 ++ modules/Kconfig.renesas_fsp | 5 + .../counter/alarm/boards/ek_ra8d1.overlay | 13 + .../counter/alarm/boards/ek_ra8m1.overlay | 13 + .../counter/alarm/boards/mck_ra8t1.overlay | 13 + samples/drivers/counter/alarm/src/main.c | 6 + 15 files changed, 756 insertions(+) create mode 100644 drivers/counter/Kconfig.renesas_ra create mode 100644 drivers/counter/counter_renesas_ra_agt.c create mode 100644 dts/bindings/counter/renesas,ra-agt-counter.yml create mode 100644 dts/bindings/misc/renesas,ra-agt.yaml create mode 100644 samples/drivers/counter/alarm/boards/ek_ra8d1.overlay create mode 100644 samples/drivers/counter/alarm/boards/ek_ra8m1.overlay create mode 100644 samples/drivers/counter/alarm/boards/mck_ra8t1.overlay 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;