All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v5 0/4] timers: Add support for RZ/N1 SoCs timers
@ 2026-09-11 15:04 Herve Codina (Schneider Electric)
  2026-09-11 15:04 ` [PATCH v5 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-11 15:04 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 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              | 484 ++++++++++++++++++
 6 files changed, 611 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 v5 1/4] dt-bindings: timer: Add the Renesas RZ/N1 timer
  2026-09-11 15:04 [PATCH v5 0/4] timers: Add support for RZ/N1 SoCs timers Herve Codina (Schneider Electric)
@ 2026-09-11 15:04 ` Herve Codina (Schneider Electric)
  2026-09-11 15:04 ` [PATCH v5 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-11 15:04 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 v5 2/4] clocksource/drivers: Add support for the Renesas RZ/N1 timers
  2026-09-11 15:04 [PATCH v5 0/4] timers: Add support for RZ/N1 SoCs timers Herve Codina (Schneider Electric)
  2026-09-11 15:04 ` [PATCH v5 1/4] dt-bindings: timer: Add the Renesas RZ/N1 timer Herve Codina (Schneider Electric)
@ 2026-09-11 15:04 ` Herve Codina (Schneider Electric)
  2026-09-11 15:18   ` sashiko-bot
  2026-09-11 15:04 ` [PATCH v5 3/4] ARM: dts: r9a06g032: Add support for timers Herve Codina (Schneider Electric)
  2026-09-11 15:04 ` [PATCH v5 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-11 15:04 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>
---
 drivers/clocksource/Kconfig      |  11 +
 drivers/clocksource/Makefile     |   1 +
 drivers/clocksource/timer-rzn1.c | 484 +++++++++++++++++++++++++++++++
 3 files changed, 496 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..a66a045309f6
--- /dev/null
+++ b/drivers/clocksource/timer-rzn1.c
@@ -0,0 +1,484 @@
+// 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.
+	 *
+	 * Also those 32-bit RZ/N1 timers are assigned per-cpu.
+	 *
+	 * Use a high rating for the 32-bit RZ/N1 timers in order to have them
+	 * chosen by the system.
+	 */
+	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 v5 3/4] ARM: dts: r9a06g032: Add support for timers
  2026-09-11 15:04 [PATCH v5 0/4] timers: Add support for RZ/N1 SoCs timers Herve Codina (Schneider Electric)
  2026-09-11 15:04 ` [PATCH v5 1/4] dt-bindings: timer: Add the Renesas RZ/N1 timer Herve Codina (Schneider Electric)
  2026-09-11 15:04 ` [PATCH v5 2/4] clocksource/drivers: Add support for the Renesas RZ/N1 timers Herve Codina (Schneider Electric)
@ 2026-09-11 15:04 ` Herve Codina (Schneider Electric)
  2026-09-11 15:04 ` [PATCH v5 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-11 15:04 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 v5 4/4] MAINTAINERS: Add the Renesas RZ/N1 timers driver entry
  2026-09-11 15:04 [PATCH v5 0/4] timers: Add support for RZ/N1 SoCs timers Herve Codina (Schneider Electric)
                   ` (2 preceding siblings ...)
  2026-09-11 15:04 ` [PATCH v5 3/4] ARM: dts: r9a06g032: Add support for timers Herve Codina (Schneider Electric)
@ 2026-09-11 15:04 ` Herve Codina (Schneider Electric)
  3 siblings, 0 replies; 6+ messages in thread
From: Herve Codina (Schneider Electric) @ 2026-09-11 15:04 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 v5 2/4] clocksource/drivers: Add support for the Renesas RZ/N1 timers
  2026-09-11 15:04 ` [PATCH v5 2/4] clocksource/drivers: Add support for the Renesas RZ/N1 timers Herve Codina (Schneider Electric)
@ 2026-09-11 15:18   ` sashiko-bot
  0 siblings, 0 replies; 6+ messages in thread
From: sashiko-bot @ 2026-09-11 15:18 UTC (permalink / raw)
  To: Herve Codina (Schneider Electric); +Cc: conor+dt, wsa+renesas, robh, devicetree

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [High] The 16-bit per-CPU timers will never be chosen over the arch timer due to their low rating, resulting in asymmetrical per-CPU tick devices.
- [Medium] Clocksource and sched_clock return raw decreasing values if the timer is a down-counter.
--

commit 0389aacef8150cde2d480116d05b7b23c573015b
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. This controller handles
8 timers (6 16-bit, 2 32-bit). Each timer has its own interrupt and prescaler.

> diff --git a/drivers/clocksource/timer-rzn1.c b/drivers/clocksource/timer-rzn1.c
> new file mode 100644
> index 0000000000000..a66a045309f63
> --- /dev/null
> +++ b/drivers/clocksource/timer-rzn1.c

[ ... ]

> +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);
> +}

[Severity: Medium]
If the timer is a down-counter, will these functions return a decreasing value?

The clocksource and sched_clock cores require monotonically increasing values.
If the timer counts down, returning the raw value could cause the
(now - last) & mask calculation to yield large forward time jumps, breaking
system timekeeping and scheduling.

[ ... ]

> +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];

[ ... ]

> +static void rzn1_timer_clkevt_init_ced(struct rzn1_timer *timer, const char *name,
> +				       int irq)
> +{

[ ... ]

> +	/*
> +	 * 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.
> +	 *
> +	 * Also those 32-bit RZ/N1 timers are assigned per-cpu.
> +	 *
> +	 * Use a high rating for the 32-bit RZ/N1 timers in order to have them
> +	 * chosen by the system.
> +	 */
> +	timer->ced.rating = timer->width == 16 ? 200 : 460;

[Severity: High]
Is it intentional that the 16-bit per-CPU timers are assigned a rating of 200?

In rzn1_local_timer_starting_cpu(), the 16-bit timers are explicitly assigned
as the per-CPU tick devices. However, because the ARM arch timer typically has
a higher rating (400-450), won't the 16-bit timers always be outranked and
ignored by the clockevents core?

This appears to contradict the comment stating that 32-bit timers are assigned
per-cpu, and could leave CPU 1 falling back to the arch timer while CPU 0
might use the global 32-bit timer, resulting in asymmetrical per-CPU tick
devices.

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260911150456.893761-1-herve.codina@bootlin.com?part=2

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-09-11 15:18 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-11 15:04 [PATCH v5 0/4] timers: Add support for RZ/N1 SoCs timers Herve Codina (Schneider Electric)
2026-09-11 15:04 ` [PATCH v5 1/4] dt-bindings: timer: Add the Renesas RZ/N1 timer Herve Codina (Schneider Electric)
2026-09-11 15:04 ` [PATCH v5 2/4] clocksource/drivers: Add support for the Renesas RZ/N1 timers Herve Codina (Schneider Electric)
2026-09-11 15:18   ` sashiko-bot
2026-09-11 15:04 ` [PATCH v5 3/4] ARM: dts: r9a06g032: Add support for timers Herve Codina (Schneider Electric)
2026-09-11 15:04 ` [PATCH v5 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.