* [PATCH v6 0/4] timers: Add support for RZ/N1 SoCs timers
@ 2026-09-29 8:50 Herve Codina (Schneider Electric)
2026-09-29 8:50 ` [PATCH v6 1/4] dt-bindings: timer: Add the Renesas RZ/N1 timer Herve Codina (Schneider Electric)
` (3 more replies)
0 siblings, 4 replies; 6+ messages in thread
From: Herve Codina (Schneider Electric) @ 2026-09-29 8:50 UTC (permalink / raw)
To: Wolfram Sang, Herve Codina, Daniel Lezcano, Thomas Gleixner,
Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Geert Uytterhoeven, Magnus Damm
Cc: linux-renesas-soc, linux-kernel, devicetree, Pascal Eberhard,
Miquel Raynal, Thomas Petazzoni
The Renesas RZ/N1 SoCs family has two timers block controller
available.
Each controller is charge of 8 timers (six 16-bit timers + two 32-bit
timers).
Each timer has its own interrupt, its own prescaler that can be used to
divide the clock by 25 (timers block are fed with a 25 MHz clock) and
all timers can work in either one-shot or periodic mode.
This series adds support for those timers blocks.
Best regards,
Hervé
Changes v5 -> v6
v5: https://lore.kernel.org/all/20260911150456.893761-1-herve.codina@bootlin.com/
Patch 2:
- Fix a comment
- Add 'Tested-by: Wolfram Sang'
Patch 1, 3 and 4:
No changes
Changes v4 -> v5
v4: https://lore.kernel.org/all/20260721082952.215691-1-herve.codina@bootlin.com/
Rebase on top of v7.3-rc1
Wolfram sent his 'Tested-by' tag for patch 2 in the v4 iteration but
because of timers re-assignment done this v5, I haven't add his tag
in patch 2.
Patch 1:
- Reduce the reg area size
- Add 'Reviewed-by: Wolfram Sang'
Patch 2:
- Take into account Shashiko's feedback and avoid paths which could
lead to a NULL pointer dereference in the interrupt handle. Even if
these paths cannot be reached with our hardware timers, remove
them from the driver.
- Reassign timers usage for the first timers instance probed:
* First and second 16-bit timers: clock events per CPU
* Last (second one) 32-bit timer: clocksource and sched_clock
* Other timers: clock events for all possible CPUs
Patch 3:
- Reduce the reg area size
- Remove some empty lines in commit log
- Add 'Reviewed-by: Wolfram Sang'
- Add 'Tested-by: Wolfram Sang'
Patch 4:
- Add 'Reviewed-by: Wolfram Sang'.
Changes v3 -> v4
v3: https://lore.kernel.org/all/20260717095552.767475-1-herve.codina@bootlin.com/
Patches 1, 3 and 4:
No changes.
Patch 2:
Took into account Shashiko's feedback
- Remove CLOCK_SOURCE_SUSPEND_NONSTOP flag
- Fix CPU ids checks against number of 32-bit timers (avoid
out-of-bound accesses)
- Enable IRQ in CPU hotpluig startup function
- Add CPU hotplug teardown function and disable IRQ in this
function.
- Adapt probe() to avoid IRQ handled by CPU hotplug startup/teardown
been enabled by default.
Changes v2 -> v3
v2: https://lore.kernel.org/all/20260429115054.158160-1-herve.codina@bootlin.com/
Rebase on top of v7.2-rc1.
Patch 1, 3 and 4:
- Add 'Reviewed-by: Geert Uytterhoeven'
Patch 2:
- Fix typos in commit log.
- Split Kconfig 'depends on' in 2 lines.
- Fix indentation, space and alignment in code.
- Fix dev_err_probe() conversion format.
- Introduce rzn1_timer_setup_timers() and rzn1_timer_register_clkevt()
to avoid code duplication in rzn1_timer_probe_{first,other}()
- Add '.suppress_bind_attrs = true'.
Changes v1 -> v2
v1: https://lore.kernel.org/all/20260331152616.197031-1-herve.codina@bootlin.com/
Rebase on top of v7.1-rc1.
Patch 1:
- Drop minItems related to interrupts property
- Add 'Reviewed-by: Krzysztof Kozlowski'
Patches 2, 3 and 4:
No changes
Herve Codina (Schneider Electric) (4):
dt-bindings: timer: Add the Renesas RZ/N1 timer
clocksource/drivers: Add support for the Renesas RZ/N1 timers
ARM: dts: r9a06g032: Add support for timers
MAINTAINERS: Add the Renesas RZ/N1 timers driver entry
.../bindings/timer/renesas,rzn1-timer.yaml | 74 +++
MAINTAINERS | 7 +
arch/arm/boot/dts/renesas/r9a06g032.dtsi | 34 ++
drivers/clocksource/Kconfig | 11 +
drivers/clocksource/Makefile | 1 +
drivers/clocksource/timer-rzn1.c | 481 ++++++++++++++++++
6 files changed, 608 insertions(+)
create mode 100644 Documentation/devicetree/bindings/timer/renesas,rzn1-timer.yaml
create mode 100644 drivers/clocksource/timer-rzn1.c
--
2.55.0
^ permalink raw reply [flat|nested] 6+ messages in thread
* [PATCH v6 1/4] dt-bindings: timer: Add the Renesas RZ/N1 timer
2026-09-29 8:50 [PATCH v6 0/4] timers: Add support for RZ/N1 SoCs timers Herve Codina (Schneider Electric)
@ 2026-09-29 8:50 ` Herve Codina (Schneider Electric)
2026-09-29 8:50 ` [PATCH v6 2/4] clocksource/drivers: Add support for the Renesas RZ/N1 timers Herve Codina (Schneider Electric)
` (2 subsequent siblings)
3 siblings, 0 replies; 6+ messages in thread
From: Herve Codina (Schneider Electric) @ 2026-09-29 8:50 UTC (permalink / raw)
To: Wolfram Sang, Herve Codina, Daniel Lezcano, Thomas Gleixner,
Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Geert Uytterhoeven, Magnus Damm
Cc: linux-renesas-soc, linux-kernel, devicetree, Pascal Eberhard,
Miquel Raynal, Thomas Petazzoni, Krzysztof Kozlowski
The Renesas RZ/N1 timer block controller is the controller in charge of
timers available in the Renesas RZ/N1 SoCs family.
This controller handles 8 timers:
- 6 16-bit timers
- 2 32-bit timers
Signed-off-by: Herve Codina (Schneider Electric) <herve.codina@bootlin.com>
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@oss.qualcomm.com>
Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
Reviewed-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
---
.../bindings/timer/renesas,rzn1-timer.yaml | 74 +++++++++++++++++++
1 file changed, 74 insertions(+)
create mode 100644 Documentation/devicetree/bindings/timer/renesas,rzn1-timer.yaml
diff --git a/Documentation/devicetree/bindings/timer/renesas,rzn1-timer.yaml b/Documentation/devicetree/bindings/timer/renesas,rzn1-timer.yaml
new file mode 100644
index 000000000000..7218c0c2671d
--- /dev/null
+++ b/Documentation/devicetree/bindings/timer/renesas,rzn1-timer.yaml
@@ -0,0 +1,74 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/timer/renesas,rzn1-timer.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Renesas RZ/N1 timers
+
+maintainers:
+ - Herve Codina <herve.codina@bootlin.com>
+
+description: |
+ The Renesas RZ/N1 SoCs timers block controller is composed of 8 independent
+ timers.
+ - 6 are 16-bit timers
+ - 2 are 32-bit timers
+
+ Each timer has its own interrupt line and can work in either one-shot or
+ periodic mode.
+
+properties:
+ compatible:
+ items:
+ - const: renesas,r9a06g032-timer # RZ/N1D
+ - const: renesas,rzn1-timer
+
+ reg:
+ maxItems: 1
+
+ clocks:
+ items:
+ - description: APB internal bus clock
+
+ clock-names:
+ items:
+ - const: pclk
+
+ power-domains:
+ maxItems: 1
+
+ interrupts:
+ maxItems: 8
+
+required:
+ - compatible
+ - reg
+ - clocks
+ - clock-names
+ - power-domains
+ - interrupts
+
+additionalProperties: false
+
+examples:
+ - |
+ #include <dt-bindings/interrupt-controller/arm-gic.h>
+ #include <dt-bindings/clock/r9a06g032-sysctrl.h>
+
+ timer@51001000 {
+ compatible = "renesas,r9a06g032-timer", "renesas,rzn1-timer";
+ reg = <0x51001000 0x200>;
+ clocks = <&sysctrl R9A06G032_HCLK_TIMER0>;
+ clock-names = "pclk";
+ power-domains = <&sysctrl>;
+ interrupts = <GIC_SPI 112 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 114 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 115 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 116 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 118 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 119 IRQ_TYPE_LEVEL_HIGH>;
+ };
+...
--
2.55.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v6 2/4] clocksource/drivers: Add support for the Renesas RZ/N1 timers
2026-09-29 8:50 [PATCH v6 0/4] timers: Add support for RZ/N1 SoCs timers Herve Codina (Schneider Electric)
2026-09-29 8:50 ` [PATCH v6 1/4] dt-bindings: timer: Add the Renesas RZ/N1 timer Herve Codina (Schneider Electric)
@ 2026-09-29 8:50 ` Herve Codina (Schneider Electric)
2026-09-29 9:06 ` sashiko-bot
2026-09-29 8:50 ` [PATCH v6 3/4] ARM: dts: r9a06g032: Add support for timers Herve Codina (Schneider Electric)
2026-09-29 8:50 ` [PATCH v6 4/4] MAINTAINERS: Add the Renesas RZ/N1 timers driver entry Herve Codina (Schneider Electric)
3 siblings, 1 reply; 6+ messages in thread
From: Herve Codina (Schneider Electric) @ 2026-09-29 8:50 UTC (permalink / raw)
To: Wolfram Sang, Herve Codina, Daniel Lezcano, Thomas Gleixner,
Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Geert Uytterhoeven, Magnus Damm
Cc: linux-renesas-soc, linux-kernel, devicetree, Pascal Eberhard,
Miquel Raynal, Thomas Petazzoni
The Renesas RZ/N1 timer block controller is the controller in charge of
timers available in the Renesas RZ/N1 SoCs family.
This controller handles 8 timers:
- 6 16-bit timers
- 2 32-bit timers
Each timer has its own interrupt, its own prescaler that can be used to
divide the clock by 25 and all of them can work in either one-shot or
periodic mode.
Signed-off-by: Herve Codina (Schneider Electric) <herve.codina@bootlin.com>
Tested-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
---
drivers/clocksource/Kconfig | 11 +
drivers/clocksource/Makefile | 1 +
drivers/clocksource/timer-rzn1.c | 481 +++++++++++++++++++++++++++++++
3 files changed, 493 insertions(+)
create mode 100644 drivers/clocksource/timer-rzn1.c
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index d9c76dd443f8..da83483296d5 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -152,6 +152,17 @@ config REALTEK_OTTO_TIMER
RT8391, RTL8392, RTL8393 and RTL8396 and chips of the RTL930x series
such as RTL9301, RTL9302 or RTL9303.
+config RZN1_TIMER
+ bool "Renesas RZ/N1 Timer"
+ depends on HAS_IOMEM && COMMON_CLK
+ depends on ARCH_RZN1 || COMPILE_TEST
+ help
+ Enables support for RZ/N1 SoC timers.
+ A timers block in RZ/N1 SoCs is composed of 8 timers
+ - 6 16-bit timers
+ - 2 32-bit timers
+ Two timers blocks are available in RZ/N1 SoCs.
+
config SUN4I_TIMER
bool "Sun4i timer driver" if COMPILE_TEST
depends on HAS_IOMEM
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index b46376af6b49..464ec6690ab6 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -96,3 +96,4 @@ obj-$(CONFIG_EP93XX_TIMER) += timer-ep93xx.o
obj-$(CONFIG_RALINK_TIMER) += timer-ralink.o
obj-$(CONFIG_NXP_STM_TIMER) += timer-nxp-stm.o
obj-$(CONFIG_RTK_SYSTIMER) += timer-realtek.o
+obj-$(CONFIG_RZN1_TIMER) += timer-rzn1.o
diff --git a/drivers/clocksource/timer-rzn1.c b/drivers/clocksource/timer-rzn1.c
new file mode 100644
index 000000000000..3b173b4086e9
--- /dev/null
+++ b/drivers/clocksource/timer-rzn1.c
@@ -0,0 +1,481 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * RZ/N1 timers driver
+ *
+ * Copyright (C) 2026 Schneider-Electric
+ *
+ * Author: Herve Codina <herve.codina@bootlin.com>
+ */
+
+#include <linux/bits.h>
+#include <linux/clk.h>
+#include <linux/clockchips.h>
+#include <linux/clocksource.h>
+#include <linux/cpuhotplug.h>
+#include <linux/interrupt.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/sched_clock.h>
+
+/* Per-timer register offsets */
+#define RZN1_TIMER_LOAD_COUNT_REG 0x00
+#define RZN1_TIMER_CURRENT_COUNT_REG 0x04
+#define RZN1_TIMER_CONTROL_REG 0x08
+#define RZN1_TIMER_CONTROL_INT_UNMASK BIT(3)
+#define RZN1_TIMER_CONTROL_ENABLE BIT(2)
+#define RZN1_TIMER_CONTROL_AUTORELOAD BIT(1)
+#define RZN1_TIMER_CONTROL_PRESCALER_DIV25 BIT(0)
+#define RZN1_TIMER_CLEAR_INT_REG 0x0c
+#define RZN1_TIMER_STATUS_INT0_REG 0x10
+#define RZN1_TIMER_STATUS_INT1_REG 0x14
+
+/*
+ * 8 timers are available. Among those 8 timers, the first 6 timers are 16-bit
+ * timers and the last two ones are 32-bit timers.
+ */
+#define RZN1_TIMER_BASE_INDEX_16BIT_TIMERS 0
+#define RZN1_TIMER_NB_16BIT_TIMERS 6
+#define RZN1_TIMER_LAST_INDEX_16BIT_TIMERS (RZN1_TIMER_BASE_INDEX_16BIT_TIMERS + \
+ RZN1_TIMER_NB_16BIT_TIMERS - 1)
+
+#define RZN1_TIMER_BASE_INDEX_32BIT_TIMERS 6
+#define RZN1_TIMER_NB_32BIT_TIMERS 2
+#define RZN1_TIMER_LAST_INDEX_32BIT_TIMERS (RZN1_TIMER_BASE_INDEX_32BIT_TIMERS + \
+ RZN1_TIMER_NB_32BIT_TIMERS - 1)
+
+#define RZN1_TIMER_IS_16BIT_TIMER(_i) ((_i) < RZN1_TIMER_BASE_INDEX_32BIT_TIMERS)
+
+/* Total numbers of timers */
+#define RZN1_TIMER_NB_TIMERS (RZN1_TIMER_NB_16BIT_TIMERS + \
+ RZN1_TIMER_NB_32BIT_TIMERS)
+
+/* Offset of registers related to the timer number _i in the timers regs area */
+#define RZN1_TIMER_REGS_OFFSET(_i) (0x20 * (_i))
+
+struct rzn1_timer {
+ void __iomem *base;
+ unsigned int width;
+ unsigned long rate;
+ unsigned long hz_period;
+ struct clock_event_device ced;
+};
+
+static int rzn1_timer_config(struct rzn1_timer *timer, u32 period, bool is_periodic)
+{
+ u32 ctrl;
+
+ ctrl = readl(timer->base + RZN1_TIMER_CONTROL_REG);
+
+ /* Disable the timer */
+ writel(ctrl & ~RZN1_TIMER_CONTROL_ENABLE,
+ timer->base + RZN1_TIMER_CONTROL_REG);
+
+ /* Set the new period */
+ writel(period, timer->base + RZN1_TIMER_LOAD_COUNT_REG);
+
+ /* Prepare the timer mode */
+ if (is_periodic)
+ ctrl |= RZN1_TIMER_CONTROL_AUTORELOAD;
+ else
+ ctrl &= ~RZN1_TIMER_CONTROL_AUTORELOAD;
+
+ /*
+ * Enable the timer.
+ *
+ * This automatically resets the CURRENT_COUNT register.
+ */
+ ctrl |= RZN1_TIMER_CONTROL_ENABLE;
+ writel(ctrl, timer->base + RZN1_TIMER_CONTROL_REG);
+
+ return 0;
+}
+
+static void rzn1_timer_disable(struct rzn1_timer *timer)
+{
+ u32 ctrl;
+
+ ctrl = readl(timer->base + RZN1_TIMER_CONTROL_REG);
+ ctrl &= ~RZN1_TIMER_CONTROL_ENABLE;
+ writel(ctrl, timer->base + RZN1_TIMER_CONTROL_REG);
+}
+
+static void rzn1_timer_int_enable(struct rzn1_timer *timer)
+{
+ u32 ctrl;
+
+ ctrl = readl(timer->base + RZN1_TIMER_CONTROL_REG);
+ ctrl |= RZN1_TIMER_CONTROL_INT_UNMASK;
+ writel(ctrl, timer->base + RZN1_TIMER_CONTROL_REG);
+}
+
+static irqreturn_t rzn1_timer_interrupt(int irq, void *dev_id)
+{
+ struct rzn1_timer *timer = dev_id;
+
+ /* Ack the interrupt */
+ readl(timer->base + RZN1_TIMER_CLEAR_INT_REG);
+
+ timer->ced.event_handler(&timer->ced);
+ return IRQ_HANDLED;
+}
+
+static int rzn1_clkevt_set_next_event(unsigned long cycles,
+ struct clock_event_device *evt)
+{
+ struct rzn1_timer *timer = container_of(evt, struct rzn1_timer, ced);
+
+ return rzn1_timer_config(timer, cycles, false);
+}
+
+static int rzn1_clkevt_shutdown(struct clock_event_device *evt)
+{
+ struct rzn1_timer *timer = container_of(evt, struct rzn1_timer, ced);
+
+ rzn1_timer_disable(timer);
+ return 0;
+}
+
+static int rzn1_clkevt_set_periodic(struct clock_event_device *evt)
+{
+ struct rzn1_timer *timer = container_of(evt, struct rzn1_timer, ced);
+
+ rzn1_timer_config(timer, timer->hz_period, true);
+ return 0;
+}
+
+/*
+ * This global lock is used to prevent race conditions during global timers
+ * test and set operation in case the driver is using the async probe option.
+ */
+static DEFINE_MUTEX(rzn1_global_timers_lock);
+
+static int rzn1_timer_instance;
+
+/* scheduler and clocksource */
+static struct rzn1_timer *rzn1_sched_clock;
+
+static u64 notrace rzn1_sched_read(void)
+{
+ return readl_relaxed(rzn1_sched_clock->base + RZN1_TIMER_CURRENT_COUNT_REG);
+}
+
+static u64 rzn1_clksrc_read(struct clocksource *cs)
+{
+ return readl_relaxed(rzn1_sched_clock->base + RZN1_TIMER_CURRENT_COUNT_REG);
+}
+
+static struct clocksource rzn1_clocksource = {
+ .name = "rzn1_timer_clocksource",
+ .rating = 200,
+ .flags = CLOCK_SOURCE_IS_CONTINUOUS,
+ .read = rzn1_clksrc_read,
+};
+
+/* Timers table where 16-bit timers will be assigned per-cpu */
+static struct rzn1_timer *rzn1_tab_timers;
+
+static int rzn1_local_timer_starting_cpu(unsigned int cpu)
+{
+ struct rzn1_timer *timer;
+
+ /* We assign the first and second 16-bit timer as per-cpu timers. */
+ if (cpu >= 2)
+ return 0;
+
+ timer = &rzn1_tab_timers[cpu + RZN1_TIMER_BASE_INDEX_16BIT_TIMERS];
+
+ timer->ced.cpumask = cpumask_of(cpu);
+ irq_force_affinity(timer->ced.irq, cpumask_of(cpu));
+
+ clockevents_config_and_register(&timer->ced, timer->rate, 1,
+ GENMASK(timer->width - 1, 0));
+ /*
+ * Time to enable the interrupt even if no interrupt can be fired yet.
+ * Indeed, the timer is still globally disabled.
+ * It will be enabled when rzn1_timer_config() is called
+ */
+ enable_irq(timer->ced.irq);
+ return 0;
+}
+
+static int rzn1_local_timer_dying_cpu(unsigned int cpu)
+{
+ struct rzn1_timer *timer;
+
+ if (cpu >= 2)
+ return 0;
+
+ timer = &rzn1_tab_timers[cpu + RZN1_TIMER_BASE_INDEX_16BIT_TIMERS];
+
+ rzn1_timer_disable(timer);
+ disable_irq_nosync(timer->ced.irq);
+ return 0;
+}
+
+static void rzn1_timer_init(struct rzn1_timer *timer, unsigned int index,
+ void __iomem *timers_base, unsigned long clock_rate)
+{
+ timer->base = timers_base + RZN1_TIMER_REGS_OFFSET(index);
+
+ /* Disable the timer */
+ writel(0, timer->base + RZN1_TIMER_CONTROL_REG);
+
+ /* Clear potential interrupts */
+ readl(timer->base + RZN1_TIMER_CLEAR_INT_REG);
+
+ if (RZN1_TIMER_IS_16BIT_TIMER(index)) {
+ timer->width = 16;
+ /*
+ * Enable pre-scaler for 16-bit timers.
+ *
+ * Timers are fed by a 25MHz clock. With this pre-scaler (1/25)
+ * enabled, the 16-bit timers resolution is 1 microsecond.
+ */
+ writel(RZN1_TIMER_CONTROL_PRESCALER_DIV25,
+ timer->base + RZN1_TIMER_CONTROL_REG);
+ timer->rate = clock_rate / 25;
+ } else {
+ timer->width = 32;
+ timer->rate = clock_rate;
+ }
+ timer->hz_period = DIV_ROUND_UP(timer->rate, HZ);
+}
+
+static void rzn1_timer_clkevt_init_ced(struct rzn1_timer *timer, const char *name,
+ int irq)
+{
+ timer->ced.features = CLOCK_EVT_FEAT_PERIODIC | CLOCK_EVT_FEAT_ONESHOT;
+ timer->ced.set_next_event = rzn1_clkevt_set_next_event;
+ timer->ced.set_state_shutdown = rzn1_clkevt_shutdown;
+ timer->ced.set_state_periodic = rzn1_clkevt_set_periodic;
+ timer->ced.cpumask = cpu_possible_mask;
+
+ /*
+ * 32-bit timers are fed with a 25 MHz clock directly derived from the
+ * main PLL (1 GHz). The arch timer, ARMv7 CP15 in RZ/N1 SoCs, is fed
+ * with to a 6.25 MHz clock.
+ *
+ * The clock of the RZ/N1 timers block is a stable one and the RZ/N1
+ * timers have a higher resolution than the ARM CP15 one.
+ *
+ * Use a high rating for the 32-bit RZ/N1 timers.
+ */
+ timer->ced.rating = timer->width == 16 ? 200 : 460;
+ timer->ced.owner = THIS_MODULE;
+ timer->ced.name = name;
+ timer->ced.irq = irq;
+}
+
+static int rzn1_timer_setup_timers(struct platform_device *pdev,
+ struct rzn1_timer *tab_timers,
+ unsigned int first_timer_index,
+ unsigned int last_timer_index,
+ void __iomem *base, unsigned long clock_rate)
+{
+ struct device *dev = &pdev->dev;
+ struct rzn1_timer *timer;
+ unsigned int i;
+ char *name;
+ int irq;
+ int ret;
+
+ for (i = first_timer_index; i <= last_timer_index; i++) {
+ timer = &tab_timers[i];
+
+ rzn1_timer_init(timer, i, base, clock_rate);
+
+ irq = platform_get_irq(pdev, i);
+ if (irq < 0)
+ return irq;
+
+ name = devm_kasprintf(dev, GFP_KERNEL, "%s-%u", dev_name(dev), i);
+ if (!name)
+ return -ENOMEM;
+
+ rzn1_timer_clkevt_init_ced(timer, name, irq);
+
+ ret = devm_request_irq(dev, timer->ced.irq, rzn1_timer_interrupt,
+ IRQF_TIMER | IRQF_NO_AUTOEN,
+ timer->ced.name, timer);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "timer%u: Failed to request IRQ\n", i);
+
+ rzn1_timer_int_enable(timer);
+ }
+ return 0;
+}
+
+static void rzn1_timer_register_clkevt(struct rzn1_timer *tab_timers,
+ unsigned int first_timer_index,
+ unsigned int last_timer_index)
+{
+ struct rzn1_timer *timer;
+ unsigned int i;
+
+ for (i = first_timer_index; i <= last_timer_index; i++) {
+ timer = &tab_timers[i];
+ clockevents_config_and_register(&timer->ced, timer->rate,
+ 1, GENMASK(timer->width - 1, 0));
+ /*
+ * Time to enable the interrupt even if no interrupt can be
+ * fired yet. Indeed, the timer is still globally disabled.
+ * It will be enabled when rzn1_timer_config() is called
+ */
+ enable_irq(timer->ced.irq);
+ }
+}
+
+static int rzn1_timer_probe_first(struct platform_device *pdev,
+ struct rzn1_timer *tab_timers,
+ void __iomem *base, unsigned long clock_rate)
+{
+ struct device *dev = &pdev->dev;
+ struct rzn1_timer *timer;
+ int ret;
+
+ /*
+ * Each timer instance has 6 x 16-bit timers and 2 x 32-bit timers.
+ *
+ * Probe the first instance. In that case, timers are assigned as
+ * follow:
+ * - First and second 16-bit timers: clock events per CPU
+ * - Last (second one) 32-bit timer: clocksource and sched_clock
+ * - Other timers: clock events for all possible CPUs
+ *
+ * First step, perform all operation that could fail without calling
+ * clockevents_config_and_register(), sched_clock_register() nor
+ * cpuhp_setup_state(). Those operation don't have unregister nor
+ * teardown counterparts and so, once called, we cannot remove the
+ * related resource.
+ */
+
+ /* First step for 16-bit timers and the first 32-bit timers. */
+ ret = rzn1_timer_setup_timers(pdev, tab_timers,
+ RZN1_TIMER_BASE_INDEX_16BIT_TIMERS,
+ RZN1_TIMER_BASE_INDEX_32BIT_TIMERS,
+ base, clock_rate);
+ if (ret)
+ return ret;
+
+ /*
+ * Second step, almost all operations that can fail have been called.
+ * Timers are ready to work. Start with the last operation that can fail,
+ * installing and invoking hotplug callbacks
+ */
+ rzn1_tab_timers = tab_timers;
+ ret = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN,
+ "clockevents/rzn1/global_timer:starting",
+ rzn1_local_timer_starting_cpu,
+ rzn1_local_timer_dying_cpu);
+ if (ret < 0)
+ return dev_err_probe(dev, ret, "Setup CPU hotplug state failed\n");
+
+ /*
+ * At this point, no more operations can fail. Perform remaining
+ * operations. Starting by handling the last (second one) 32-bit timer
+ */
+ timer = &tab_timers[RZN1_TIMER_LAST_INDEX_32BIT_TIMERS];
+
+ rzn1_timer_init(timer, RZN1_TIMER_LAST_INDEX_32BIT_TIMERS, base, clock_rate);
+ rzn1_timer_config(timer, GENMASK(timer->width - 1, 0), true);
+
+ rzn1_sched_clock = timer;
+
+ sched_clock_register(rzn1_sched_read, rzn1_sched_clock->width, rzn1_sched_clock->rate);
+
+ rzn1_clocksource.mask = CLOCKSOURCE_MASK(rzn1_sched_clock->width);
+ clocksource_register_hz(&rzn1_clocksource, rzn1_sched_clock->rate);
+
+ /*
+ * Register clockevents for 16-bit timers (except the first and second
+ * ones) and for the first 32-bit timer. The first and second 16-bit
+ * timers clockevents are registered by CPU hotplug startup function set
+ * previously by the cpuhp_setup_state() call.
+ */
+ rzn1_timer_register_clkevt(tab_timers,
+ RZN1_TIMER_BASE_INDEX_16BIT_TIMERS + 2,
+ RZN1_TIMER_BASE_INDEX_32BIT_TIMERS);
+
+ return 0;
+}
+
+static int rzn1_timer_probe_other(struct platform_device *pdev,
+ struct rzn1_timer *tab_timers,
+ void __iomem *base, unsigned long clock_rate)
+{
+ int ret;
+
+ /*
+ * Probe other instance(s), i.e. not the first one. In that case,
+ * all timers are used as clock events and available for all possible
+ * CPUs
+ *
+ * First step, perform all operation that could fail without calling
+ * clockevents_config_and_register(). Unregister counterpart does not
+ * exist and so, once called, we cannot remove the related resource.
+ */
+ ret = rzn1_timer_setup_timers(pdev, tab_timers,
+ RZN1_TIMER_BASE_INDEX_16BIT_TIMERS,
+ RZN1_TIMER_LAST_INDEX_32BIT_TIMERS,
+ base, clock_rate);
+ if (ret)
+ return ret;
+
+ /*
+ * Second step, all operation that can fail have been called. We can
+ * register our timers.
+ */
+ rzn1_timer_register_clkevt(tab_timers,
+ RZN1_TIMER_BASE_INDEX_16BIT_TIMERS,
+ RZN1_TIMER_LAST_INDEX_32BIT_TIMERS);
+ return 0;
+}
+
+static int rzn1_timer_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct rzn1_timer *tab_timers;
+ unsigned long clock_rate;
+ void __iomem *base;
+ struct clk *clk;
+ int ret;
+
+ tab_timers = devm_kcalloc(dev, RZN1_TIMER_NB_TIMERS, sizeof(*tab_timers),
+ GFP_KERNEL);
+ if (!tab_timers)
+ return -ENOMEM;
+
+ base = devm_platform_ioremap_resource(pdev, 0);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ clk = devm_clk_get_enabled(dev, "pclk");
+ if (IS_ERR(clk))
+ return dev_err_probe(dev, PTR_ERR(clk), "Failed to get pclk\n");
+
+ clock_rate = clk_get_rate(clk);
+
+ scoped_guard(mutex, &rzn1_global_timers_lock) {
+ ret = !rzn1_timer_instance ?
+ rzn1_timer_probe_first(pdev, tab_timers, base, clock_rate) :
+ rzn1_timer_probe_other(pdev, tab_timers, base, clock_rate);
+ if (!ret)
+ rzn1_timer_instance++;
+ }
+
+ return ret;
+}
+
+static const struct of_device_id rzn1_timer_of_match[] = {
+ { .compatible = "renesas,rzn1-timer", },
+ { /* sentinel */ }
+};
+
+static struct platform_driver rzn1_timer_driver = {
+ .driver = {
+ .name = "rzn1_timer",
+ .of_match_table = rzn1_timer_of_match,
+ .suppress_bind_attrs = true,
+ },
+};
+builtin_platform_driver_probe(rzn1_timer_driver, rzn1_timer_probe);
--
2.55.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v6 3/4] ARM: dts: r9a06g032: Add support for timers
2026-09-29 8:50 [PATCH v6 0/4] timers: Add support for RZ/N1 SoCs timers Herve Codina (Schneider Electric)
2026-09-29 8:50 ` [PATCH v6 1/4] dt-bindings: timer: Add the Renesas RZ/N1 timer Herve Codina (Schneider Electric)
2026-09-29 8:50 ` [PATCH v6 2/4] clocksource/drivers: Add support for the Renesas RZ/N1 timers Herve Codina (Schneider Electric)
@ 2026-09-29 8:50 ` Herve Codina (Schneider Electric)
2026-09-29 8:50 ` [PATCH v6 4/4] MAINTAINERS: Add the Renesas RZ/N1 timers driver entry Herve Codina (Schneider Electric)
3 siblings, 0 replies; 6+ messages in thread
From: Herve Codina (Schneider Electric) @ 2026-09-29 8:50 UTC (permalink / raw)
To: Wolfram Sang, Herve Codina, Daniel Lezcano, Thomas Gleixner,
Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Geert Uytterhoeven, Magnus Damm
Cc: linux-renesas-soc, linux-kernel, devicetree, Pascal Eberhard,
Miquel Raynal, Thomas Petazzoni
In the Renesas RZ/N1 SoCs family, two timers block are available. Each
block contains 8 timers composed of 6 16-bits timers and 2 32-bits
timers. Each timer has its own interrupt line.
Describe those timers blocks.
Signed-off-by: Herve Codina (Schneider Electric) <herve.codina@bootlin.com>
Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
Reviewed-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
Tested-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
---
arch/arm/boot/dts/renesas/r9a06g032.dtsi | 34 ++++++++++++++++++++++++
1 file changed, 34 insertions(+)
diff --git a/arch/arm/boot/dts/renesas/r9a06g032.dtsi b/arch/arm/boot/dts/renesas/r9a06g032.dtsi
index 19c9bce0a26d..6fae6db2850e 100644
--- a/arch/arm/boot/dts/renesas/r9a06g032.dtsi
+++ b/arch/arm/boot/dts/renesas/r9a06g032.dtsi
@@ -806,6 +806,40 @@ gpioirqmux: interrupt-controller@51000480 {
status = "disabled";
};
+ timer0: timer@51001000 {
+ compatible = "renesas,r9a06g032-timer", "renesas,rzn1-timer";
+ reg = <0x51001000 0x200>;
+ clocks = <&sysctrl R9A06G032_HCLK_TIMER0>;
+ clock-names = "pclk";
+ power-domains = <&sysctrl>;
+ interrupts = <GIC_SPI 112 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 113 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 114 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 115 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 116 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 117 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 118 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 119 IRQ_TYPE_LEVEL_HIGH>;
+ status = "disabled";
+ };
+
+ timer1: timer@51002000 {
+ compatible = "renesas,r9a06g032-timer", "renesas,rzn1-timer";
+ reg = <0x51002000 0x200>;
+ clocks = <&sysctrl R9A06G032_HCLK_TIMER1>;
+ clock-names = "pclk";
+ power-domains = <&sysctrl>;
+ interrupts = <GIC_SPI 120 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 121 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 122 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 123 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 124 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 125 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 126 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 127 IRQ_TYPE_LEVEL_HIGH>;
+ status = "disabled";
+ };
+
can0: can@52104000 {
compatible = "renesas,r9a06g032-sja1000", "renesas,rzn1-sja1000";
reg = <0x52104000 0x800>;
--
2.55.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* [PATCH v6 4/4] MAINTAINERS: Add the Renesas RZ/N1 timers driver entry
2026-09-29 8:50 [PATCH v6 0/4] timers: Add support for RZ/N1 SoCs timers Herve Codina (Schneider Electric)
` (2 preceding siblings ...)
2026-09-29 8:50 ` [PATCH v6 3/4] ARM: dts: r9a06g032: Add support for timers Herve Codina (Schneider Electric)
@ 2026-09-29 8:50 ` Herve Codina (Schneider Electric)
3 siblings, 0 replies; 6+ messages in thread
From: Herve Codina (Schneider Electric) @ 2026-09-29 8:50 UTC (permalink / raw)
To: Wolfram Sang, Herve Codina, Daniel Lezcano, Thomas Gleixner,
Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Geert Uytterhoeven, Magnus Damm
Cc: linux-renesas-soc, linux-kernel, devicetree, Pascal Eberhard,
Miquel Raynal, Thomas Petazzoni
After contributing the driver, add myself as the maintainer for the
Renesas RZ/N1 timers driver.
Signed-off-by: Herve Codina (Schneider Electric) <herve.codina@bootlin.com>
Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be>
Reviewed-by: Wolfram Sang <wsa+renesas@sang-engineering.com>
---
MAINTAINERS | 7 +++++++
1 file changed, 7 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 3a19da74d00c..91b014d27577 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -23296,6 +23296,13 @@ S: Maintained
F: Documentation/devicetree/bindings/rtc/renesas,rzn1-rtc.yaml
F: drivers/rtc/rtc-rzn1.c
+RENESAS RZ/N1 TIMERS DRIVER
+M: Herve Codina <herve.codina@bootlin.com>
+L: linux-renesas-soc@vger.kernel.org
+S: Maintained
+F: Documentation/devicetree/bindings/timer/renesas,rzn1-timer.yaml
+F: drivers/clocksource/timer-rzn1.c
+
RENESAS RZ/N1 USBF CONTROLLER DRIVER
M: Herve Codina <herve.codina@bootlin.com>
L: linux-renesas-soc@vger.kernel.org
--
2.55.0
^ permalink raw reply related [flat|nested] 6+ messages in thread
* Re: [PATCH v6 2/4] clocksource/drivers: Add support for the Renesas RZ/N1 timers
2026-09-29 8:50 ` [PATCH v6 2/4] clocksource/drivers: Add support for the Renesas RZ/N1 timers Herve Codina (Schneider Electric)
@ 2026-09-29 9:06 ` sashiko-bot
0 siblings, 0 replies; 6+ messages in thread
From: sashiko-bot @ 2026-09-29 9:06 UTC (permalink / raw)
To: Herve Codina (Schneider Electric); +Cc: devicetree, robh, wsa+renesas, conor+dt
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] timer: rzn1: NULL pointer deref in irq handler for detached timers
--
commit c504b8f1cef80ec82616cd19b137210a244f7ec4
Author: Herve Codina (Schneider Electric) <herve.codina@bootlin.com>
clocksource/drivers: Add support for the Renesas RZ/N1 timers
The Renesas RZ/N1 timer block controller is the controller in charge of
timers available in the Renesas RZ/N1 SoCs family.
> diff --git a/drivers/clocksource/timer-rzn1.c b/drivers/clocksource/timer-rzn1.c
> new file mode 100644
> index 0000000000000..3b173b4086e92
> --- /dev/null
> +++ b/drivers/clocksource/timer-rzn1.c
[ ... ]
> +static irqreturn_t rzn1_timer_interrupt(int irq, void *dev_id)
> +{
> + struct rzn1_timer *timer = dev_id;
> +
> + /* Ack the interrupt */
> + readl(timer->base + RZN1_TIMER_CLEAR_INT_REG);
> +
> + timer->ced.event_handler(&timer->ced);
[Severity: High]
Does this code risk a NULL pointer dereference if the timer is detached?
The driver unconditionally registers multiple clockevent devices and their IRQs.
The clockevents core leaves unused timers in CLOCK_EVT_STATE_DETACHED, which
means their event_handler remains NULL.
> + return IRQ_HANDLED;
> +}
[ ... ]
> +static void rzn1_timer_register_clkevt(struct rzn1_timer *tab_timers,
> + unsigned int first_timer_index,
> + unsigned int last_timer_index)
> +{
> + struct rzn1_timer *timer;
> + unsigned int i;
> +
> + for (i = first_timer_index; i <= last_timer_index; i++) {
> + timer = &tab_timers[i];
> + clockevents_config_and_register(&timer->ced, timer->rate,
> + 1, GENMASK(timer->width - 1, 0));
> + /*
> + * Time to enable the interrupt even if no interrupt can be
> + * fired yet. Indeed, the timer is still globally disabled.
> + * It will be enabled when rzn1_timer_config() is called
> + */
> + enable_irq(timer->ced.irq);
[Severity: High]
Can enabling the IRQ unconditionally here trigger the NULL pointer dereference
above?
If the clockevents core leaves a timer unused and we enable its IRQ anyway,
a spurious interrupt on this hardware line will cause the interrupt handler to
blindly call timer->ced.event_handler and crash.
> + }
> +}
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260929085035.57609-1-herve.codina@bootlin.com?part=2
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2026-09-29 9:06 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-29 8:50 [PATCH v6 0/4] timers: Add support for RZ/N1 SoCs timers Herve Codina (Schneider Electric)
2026-09-29 8:50 ` [PATCH v6 1/4] dt-bindings: timer: Add the Renesas RZ/N1 timer Herve Codina (Schneider Electric)
2026-09-29 8:50 ` [PATCH v6 2/4] clocksource/drivers: Add support for the Renesas RZ/N1 timers Herve Codina (Schneider Electric)
2026-09-29 9:06 ` sashiko-bot
2026-09-29 8:50 ` [PATCH v6 3/4] ARM: dts: r9a06g032: Add support for timers Herve Codina (Schneider Electric)
2026-09-29 8:50 ` [PATCH v6 4/4] MAINTAINERS: Add the Renesas RZ/N1 timers driver entry Herve Codina (Schneider Electric)
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.