driver: timer: Support for RX system timer
This commit add a system timer driver for Renesas RX using the CMT peripheral. The driver supports both system ticks and high-resolution cycle counting - Configures CMT0 as the system tick timer - Configures CMT1 as a free-running cycle timer for precise time tracking - Handles timer overflows to maintain a continuous cycle count. - Implements sys_clock_cycle_get_32() and sys_clock_cycle_get_64() for high-resolution timing - Supports Zephyr tickless kernel mode by tracking elapsed cycles - Enables interrupt-based tick announcement using CMT0 Signed-off-by: Duy Nguyen <duy.nguyen.xa@renesas.com> Signed-off-by: Yuichi Nakada <yuichi.nakada.sx@renesas.com>
This commit is contained in:
parent
2f0715262d
commit
ad42e4d87d
9 changed files with 332 additions and 7 deletions
|
@ -32,6 +32,7 @@ zephyr_library_sources_ifdef(CONFIG_NATIVE_SIM_TIMER native_sim_timer.c)
|
|||
zephyr_library_sources_ifdef(CONFIG_NPCX_ITIM_TIMER npcx_itim_timer.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_NRF_GRTC_TIMER nrf_grtc_timer.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_NRF_RTC_TIMER nrf_rtc_timer.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_RENESAS_RX_TIMER_CMT renesas_rx_cmt.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_RCAR_CMT_TIMER rcar_cmt_timer.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_RENESAS_RA_ULPT_TIMER renesas_ra_ulpt_timer.c)
|
||||
zephyr_library_sources_ifdef(CONFIG_RISCV_MACHINE_TIMER riscv_machine_timer.c)
|
||||
|
|
|
@ -90,6 +90,7 @@ source "drivers/timer/Kconfig.npcx_itim"
|
|||
source "drivers/timer/Kconfig.nrf_rtc"
|
||||
source "drivers/timer/Kconfig.nrf_grtc"
|
||||
source "drivers/timer/Kconfig.nrf_xrtc"
|
||||
source "drivers/timer/Kconfig.renesas_rx"
|
||||
source "drivers/timer/Kconfig.rcar_cmt"
|
||||
source "drivers/timer/Kconfig.riscv_machine"
|
||||
source "drivers/timer/Kconfig.rv32m1_lptmr"
|
||||
|
|
13
drivers/timer/Kconfig.renesas_rx
Normal file
13
drivers/timer/Kconfig.renesas_rx
Normal file
|
@ -0,0 +1,13 @@
|
|||
# Copyright (c) 2024 Renesas Electronics Corporation
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
config RENESAS_RX_TIMER_CMT
|
||||
bool "Renesas RX timer CMT"
|
||||
default y
|
||||
depends on DT_HAS_RENESAS_RX_TIMER_CMT_ENABLED
|
||||
select TIMER_HAS_64BIT_CYCLE_COUNTER
|
||||
select TICKLESS_CAPABLE
|
||||
help
|
||||
This module implements a kernel device driver for the Renesas RX
|
||||
platform provides the standard "system clock driver" interfaces.
|
||||
If unchecked, no timer will be used.
|
227
drivers/timer/renesas_rx_cmt.c
Normal file
227
drivers/timer/renesas_rx_cmt.c
Normal file
|
@ -0,0 +1,227 @@
|
|||
/*
|
||||
* Copyright (c) 2024 Renesas Electronics Corporation
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "zephyr/sys_clock.h"
|
||||
#include <zephyr/arch/cpu.h>
|
||||
#include <zephyr/init.h>
|
||||
#include <soc.h>
|
||||
#include <zephyr/drivers/clock_control.h>
|
||||
#include <zephyr/drivers/timer/system_timer.h>
|
||||
#include <zephyr/irq.h>
|
||||
#include <zephyr/spinlock.h>
|
||||
#include <zephyr/drivers/clock_control/renesas_rx_cgc.h>
|
||||
|
||||
#define DT_DRV_COMPAT renesas_rx_timer_cmt
|
||||
|
||||
#define CMT_NODE DT_NODELABEL(cmt)
|
||||
#define CMT0_NODE DT_NODELABEL(cmt0)
|
||||
#define CMT1_NODE DT_NODELABEL(cmt1)
|
||||
|
||||
#define ICU_NODE DT_NODELABEL(icu)
|
||||
|
||||
#define ICU_IR_ADDR DT_REG_ADDR_BY_NAME(ICU_NODE, IR)
|
||||
#define ICU_IER_ADDR DT_REG_ADDR_BY_NAME(ICU_NODE, IER)
|
||||
#define ICU_IPR_ADDR DT_REG_ADDR_BY_NAME(ICU_NODE, IPR)
|
||||
#define ICU_FIR_ADDR DT_REG_ADDR_BY_NAME(ICU_NODE, FIR)
|
||||
#define ICU_IR ((volatile uint8_t *)ICU_IR_ADDR)
|
||||
|
||||
#define CMT0_IRQ_NUM DT_IRQ_BY_NAME(CMT0_NODE, cmi, irq)
|
||||
#define CMT1_IRQ_NUM DT_IRQ_BY_NAME(CMT1_NODE, cmi, irq)
|
||||
|
||||
#define COUNTER_MAX 0x0000ffff
|
||||
|
||||
#define CYCLES_PER_SEC (CONFIG_SYS_CLOCK_HW_CYCLES_PER_SEC)
|
||||
#define TICKS_PER_SEC (CONFIG_SYS_CLOCK_TICKS_PER_SEC)
|
||||
#define CYCLES_PER_TICK (CYCLES_PER_SEC / TICKS_PER_SEC)
|
||||
|
||||
#define CYCLES_CYCLE_TIMER (COUNTER_MAX + 1)
|
||||
#define MSTPCR_ADDR ((volatile uint32_t *)0x00080010) /* MSTPCR register address */
|
||||
#define MSTP_CMT0_BIT (1 << 15) /* Bit 15 controls MSTPA15 (CMT0) */
|
||||
|
||||
static const struct clock_control_rx_subsys_cfg cmt_clk_cfg = {
|
||||
.mstp = DT_CLOCKS_CELL_BY_IDX(DT_INST_PARENT(0), 0, mstp),
|
||||
.stop_bit = DT_CLOCKS_CELL_BY_IDX(DT_INST_PARENT(0), 0, stop_bit),
|
||||
};
|
||||
|
||||
struct timer_rx_cfg {
|
||||
volatile uint16_t *cmstr;
|
||||
volatile uint16_t *cmcr;
|
||||
volatile uint16_t *cmcnt;
|
||||
volatile uint16_t *cmcor;
|
||||
};
|
||||
|
||||
static const struct timer_rx_cfg tick_timer_cfg = {
|
||||
.cmstr = (uint16_t *)DT_REG_ADDR_BY_NAME(CMT_NODE, CMSTR0),
|
||||
.cmcr = (uint16_t *)DT_REG_ADDR_BY_NAME(CMT0_NODE, CMCR),
|
||||
.cmcnt = (uint16_t *)DT_REG_ADDR_BY_NAME(CMT0_NODE, CMCNT),
|
||||
.cmcor = (uint16_t *)DT_REG_ADDR_BY_NAME(CMT0_NODE, CMCOR)};
|
||||
|
||||
static const struct timer_rx_cfg cycle_timer_cfg = {
|
||||
.cmstr = (uint16_t *)DT_REG_ADDR_BY_NAME(CMT_NODE, CMSTR0),
|
||||
.cmcr = (uint16_t *)DT_REG_ADDR_BY_NAME(CMT1_NODE, CMCR),
|
||||
.cmcnt = (uint16_t *)DT_REG_ADDR_BY_NAME(CMT1_NODE, CMCNT),
|
||||
.cmcor = (uint16_t *)DT_REG_ADDR_BY_NAME(CMT1_NODE, CMCOR)};
|
||||
|
||||
#ifdef CONFIG_TIMER_HAS_64BIT_CYCLE_COUNTER
|
||||
typedef uint64_t cycle_t;
|
||||
#define CYCLE_COUNT_MAX (0xffffffffffffffff)
|
||||
#else
|
||||
typedef uint32_t cycle_t;
|
||||
#define CYCLE_COUNT_MAX (0xffffffff)
|
||||
#endif
|
||||
static cycle_t cycle_count;
|
||||
|
||||
static uint16_t clock_cycles_per_tick;
|
||||
|
||||
static volatile cycle_t announced_cycle_count;
|
||||
|
||||
static struct k_spinlock lock;
|
||||
|
||||
#if defined(CONFIG_TEST)
|
||||
const int32_t z_sys_timer_irq_for_test = CMT0_IRQ_NUM;
|
||||
#endif
|
||||
|
||||
static cycle_t cmt1_elapsed(void)
|
||||
{
|
||||
uint32_t val1 = (uint32_t)(*cycle_timer_cfg.cmcnt);
|
||||
uint8_t cmt_ir = ICU_IR[29];
|
||||
uint32_t val2 = (uint32_t)(*cycle_timer_cfg.cmcnt);
|
||||
|
||||
if ((1 == cmt_ir) || (val1 > val2)) {
|
||||
cycle_count += CYCLES_CYCLE_TIMER;
|
||||
|
||||
ICU_IR[29] = 0;
|
||||
}
|
||||
|
||||
return (val2 + cycle_count);
|
||||
}
|
||||
|
||||
uint32_t sys_clock_cycle_get_32(void)
|
||||
{
|
||||
k_spinlock_key_t key = k_spin_lock(&lock);
|
||||
|
||||
uint32_t ret = (uint32_t)cmt1_elapsed();
|
||||
|
||||
k_spin_unlock(&lock, key);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_TIMER_HAS_64BIT_CYCLE_COUNTER
|
||||
uint64_t sys_clock_cycle_get_64(void)
|
||||
{
|
||||
k_spinlock_key_t key = k_spin_lock(&lock);
|
||||
|
||||
uint64_t ret = (uint64_t)cmt1_elapsed();
|
||||
|
||||
k_spin_unlock(&lock, key);
|
||||
|
||||
return ret;
|
||||
}
|
||||
#endif /* CONFIG_TIMER_HAS_64BIT_CYCLE_COUNTER */
|
||||
|
||||
uint32_t sys_clock_elapsed(void)
|
||||
{
|
||||
if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
|
||||
/* Always return 0 for tickful operation */
|
||||
return 0;
|
||||
}
|
||||
|
||||
k_spinlock_key_t key = k_spin_lock(&lock);
|
||||
|
||||
uint32_t ret;
|
||||
cycle_t current_cycle_count;
|
||||
|
||||
current_cycle_count = cmt1_elapsed();
|
||||
|
||||
if (current_cycle_count < announced_cycle_count) {
|
||||
/* cycle_count overflowed */
|
||||
ret = (uint32_t)((current_cycle_count +
|
||||
(CYCLE_COUNT_MAX - announced_cycle_count + 1)) /
|
||||
CYCLES_PER_TICK);
|
||||
} else {
|
||||
ret = (uint32_t)((current_cycle_count - announced_cycle_count) / CYCLES_PER_TICK);
|
||||
}
|
||||
|
||||
k_spin_unlock(&lock, key);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void cmt0_isr(void)
|
||||
{
|
||||
k_ticks_t dticks;
|
||||
cycle_t current_cycle_count;
|
||||
|
||||
k_spinlock_key_t key = k_spin_lock(&lock);
|
||||
|
||||
current_cycle_count = cmt1_elapsed();
|
||||
|
||||
if (current_cycle_count < announced_cycle_count) {
|
||||
/* cycle_count overflowed */
|
||||
dticks = (k_ticks_t)((current_cycle_count +
|
||||
(CYCLE_COUNT_MAX - announced_cycle_count + 1)) /
|
||||
CYCLES_PER_TICK);
|
||||
} else {
|
||||
dticks = (k_ticks_t)((current_cycle_count - announced_cycle_count) /
|
||||
CYCLES_PER_TICK);
|
||||
}
|
||||
|
||||
announced_cycle_count = (current_cycle_count / CYCLES_PER_TICK) * CYCLES_PER_TICK;
|
||||
|
||||
k_spin_unlock(&lock, key);
|
||||
|
||||
if (!IS_ENABLED(CONFIG_TICKLESS_KERNEL)) {
|
||||
sys_clock_announce(1);
|
||||
} else {
|
||||
sys_clock_announce(dticks);
|
||||
}
|
||||
}
|
||||
|
||||
static int sys_clock_driver_init(void)
|
||||
{
|
||||
const struct device *clk = DEVICE_DT_GET(DT_CLOCKS_CTLR(DT_INST_PARENT(0)));
|
||||
int ret;
|
||||
|
||||
if (!device_is_ready(clk)) {
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = clock_control_on(clk, (clock_control_subsys_t)&cmt_clk_cfg);
|
||||
if (ret < 0) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
*tick_timer_cfg.cmcr = 0x00C0; /* enable CMT0 interrupt */
|
||||
*cycle_timer_cfg.cmcr = 0x00C0; /* enable CMT1 interrupt */
|
||||
|
||||
clock_cycles_per_tick = (uint16_t)(CYCLES_PER_TICK);
|
||||
*tick_timer_cfg.cmcor = clock_cycles_per_tick - 1;
|
||||
*cycle_timer_cfg.cmcor = (uint16_t)COUNTER_MAX;
|
||||
|
||||
IRQ_CONNECT(CMT0_IRQ_NUM, 0x01, cmt0_isr, NULL, 0);
|
||||
irq_enable(CMT0_IRQ_NUM);
|
||||
|
||||
*tick_timer_cfg.cmstr = 0x0003; /* start cmt0,1 */
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void sys_clock_set_timeout(int32_t ticks, bool idle)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_TICKLESS_KERNEL) && idle && ticks == K_TICKS_FOREVER) {
|
||||
*tick_timer_cfg.cmstr = 0x0000; /* stop cmt0,1 */
|
||||
return;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_TICKLESS_KERNEL)
|
||||
/*
|
||||
* By default, it is implemented as a no operation.
|
||||
* Implement the processing as needed.
|
||||
*/
|
||||
#endif /* CONFIG_TICKLESS_KERNEL */
|
||||
}
|
||||
|
||||
SYS_INIT(sys_clock_driver_init, PRE_KERNEL_2, CONFIG_SYSTEM_CLOCK_INIT_PRIORITY);
|
23
dts/bindings/timer/renesas,rx-timer-cmt-start-control.yaml
Normal file
23
dts/bindings/timer/renesas,rx-timer-cmt-start-control.yaml
Normal file
|
@ -0,0 +1,23 @@
|
|||
# Copyright (c) 2021 KT-Elektronik, Klaucke und Partner GmbH
|
||||
# Copyright (c) 2024 Renesas Electronics Corporation
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: Renesas RX timer node
|
||||
|
||||
compatible: "renesas,rx-timer-cmt-start-control"
|
||||
|
||||
include: base.yaml
|
||||
|
||||
properties:
|
||||
reg:
|
||||
required: true
|
||||
|
||||
clocks:
|
||||
required: true
|
||||
|
||||
clock-frequency:
|
||||
type: int
|
||||
required: true
|
||||
description: |
|
||||
Clock frequency information for Timer operation
|
||||
This value need to be set at PCLKB/8
|
13
dts/bindings/timer/renesas,rx-timer-cmt.yaml
Normal file
13
dts/bindings/timer/renesas,rx-timer-cmt.yaml
Normal file
|
@ -0,0 +1,13 @@
|
|||
# Copyright (c) 2021 KT-Elektronik, Klaucke und Partner GmbH
|
||||
# Copyright (c) 2024 Renesas Electronics Corporation
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
description: Renesas RX timer node
|
||||
|
||||
compatible: "renesas,rx-timer-cmt"
|
||||
|
||||
include: base.yaml
|
||||
|
||||
properties:
|
||||
reg:
|
||||
required: true
|
|
@ -71,13 +71,38 @@
|
|||
};
|
||||
};
|
||||
|
||||
cmt0: timer@88004 {
|
||||
compatible = "renesas,rx-timer-cmt";
|
||||
reg = <0x00088004 0x02>,
|
||||
<0x00088000 0x02>,
|
||||
<0x00088002 0x02>,
|
||||
<0x00088006 0x02>;
|
||||
status = "disabled";
|
||||
cmt: timer@88000 {
|
||||
compatible = "renesas,rx-timer-cmt-start-control";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
reg = <0x00088000 0x02>;
|
||||
clocks = <&pclkb MSTPA 15>;
|
||||
reg-names = "CMSTR0";
|
||||
status = "okay";
|
||||
|
||||
cmt0: timer@88002 {
|
||||
compatible = "renesas,rx-timer-cmt";
|
||||
#address-cells = <1>;
|
||||
#size-cells = <1>;
|
||||
reg = <0x00088002 0x02>,
|
||||
<0x00088004 0x02>,
|
||||
<0x00088006 0x02>;
|
||||
reg-names = "CMCR", "CMCNT", "CMCOR";
|
||||
interrupts = <28 1>;
|
||||
interrupt-names = "cmi";
|
||||
status = "okay";
|
||||
};
|
||||
|
||||
cmt1: timer@88008 {
|
||||
compatible = "renesas,rx-timer-cmt";
|
||||
reg = <0x00088008 0x02>,
|
||||
<0x0008800A 0x02>,
|
||||
<0x0008800C 0x02>;
|
||||
reg-names = "CMCR", "CMCNT", "CMCOR";
|
||||
interrupts = <29 1>;
|
||||
interrupt-names = "cmi";
|
||||
status = "disabled";
|
||||
};
|
||||
};
|
||||
|
||||
ofsm: ofsm@ffffff80 {
|
||||
|
|
|
@ -3,6 +3,17 @@
|
|||
|
||||
if SOC_SERIES_RX130
|
||||
|
||||
DT_CMT_PATH := $(dt_nodelabel_path,cmt)
|
||||
|
||||
config SYS_CLOCK_HW_CYCLES_PER_SEC
|
||||
default $(dt_node_int_prop_int,$(DT_CMT_PATH),clock-frequency)
|
||||
|
||||
# SYS_CLOCK_TICKS_PER_SEC is set to 100 if PCLKB is 48MHz or less.
|
||||
# (PCLKB = SYS_CLOCK_HW_CYCLES_PER_SEC * 8)
|
||||
config SYS_CLOCK_TICKS_PER_SEC
|
||||
default 100 if SYS_CLOCK_HW_CYCLES_PER_SEC <= 6000000
|
||||
default 1000
|
||||
|
||||
config INITIALIZATION_STACK_SIZE
|
||||
default 512
|
||||
|
||||
|
|
|
@ -3,6 +3,17 @@
|
|||
|
||||
if SOC_SERIES_RX62N
|
||||
|
||||
DT_CMT_PATH := $(dt_nodelabel_path,cmt)
|
||||
|
||||
config SYS_CLOCK_HW_CYCLES_PER_SEC
|
||||
default $(dt_node_int_prop_int,$(DT_CMT_PATH),clock-frequency)
|
||||
|
||||
# SYS_CLOCK_TICKS_PER_SEC is set to 100 if PCLKB is 48MHz or less.
|
||||
# (PCLKB = SYS_CLOCK_HW_CYCLES_PER_SEC * 8)
|
||||
config SYS_CLOCK_TICKS_PER_SEC
|
||||
default 100 if SYS_CLOCK_HW_CYCLES_PER_SEC <= 6000000
|
||||
default 10000
|
||||
|
||||
config INITIALIZATION_STACK_SIZE
|
||||
default 512
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue