* [PATCH v7 0/4] timers: Add support for RZ/N1 SoCs timers
@ 2026-10-01 8:22 Herve Codina (Schneider Electric)
2026-10-01 8:22 ` [PATCH v7 1/4] dt-bindings: timer: Add the Renesas RZ/N1 timer Herve Codina (Schneider Electric)
` (4 more replies)
0 siblings, 5 replies; 10+ messages in thread
From: Herve Codina (Schneider Electric) @ 2026-10-01 8:22 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 v6 -> v7
v6: https://lore.kernel.org/all/20260929085035.57609-1-herve.codina@bootlin.com/
Patch 2:
- Fix a NULL pointer dereference issue reported by Sashiko in
rzn1_timer_interrupt() [0].
Even if the issue cannot be triggered with the hardware used,
avoid the potential NULL pointer dereference in the code itself.
[0] https://lore.kernel.org/all/20260929090618.011BA1F000FF@smtp.kernel.org/
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 | 483 ++++++++++++++++++
6 files changed, 610 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] 10+ messages in thread* [PATCH v7 1/4] dt-bindings: timer: Add the Renesas RZ/N1 timer 2026-10-01 8:22 [PATCH v7 0/4] timers: Add support for RZ/N1 SoCs timers Herve Codina (Schneider Electric) @ 2026-10-01 8:22 ` Herve Codina (Schneider Electric) 2026-10-14 20:20 ` [clocksource: timers/drivers/next] " clocksource-bot for Herve Codina (Schneider Electric) 2026-10-01 8:22 ` [PATCH v7 2/4] clocksource/drivers: Add support for the Renesas RZ/N1 timers Herve Codina (Schneider Electric) ` (3 subsequent siblings) 4 siblings, 1 reply; 10+ messages in thread From: Herve Codina (Schneider Electric) @ 2026-10-01 8:22 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 [flat|nested] 10+ messages in thread
* [clocksource: timers/drivers/next] dt-bindings: timer: Add the Renesas RZ/N1 timer 2026-10-01 8:22 ` [PATCH v7 1/4] dt-bindings: timer: Add the Renesas RZ/N1 timer Herve Codina (Schneider Electric) @ 2026-10-14 20:20 ` clocksource-bot for Herve Codina (Schneider Electric) 0 siblings, 0 replies; 10+ messages in thread From: clocksource-bot for Herve Codina (Schneider Electric) @ 2026-10-14 20:20 UTC (permalink / raw) To: linux-kernel Cc: Herve Codina (Schneider Electric), Daniel Lezcano, Krzysztof Kozlowski, Geert Uytterhoeven, Wolfram Sang, daniel.lezcano The following commit has been merged into the timers/drivers/next branch of clocksource: Commit-ID: 9fea79f5948bd5e0bea1cf773fd7742a6855017b Gitweb: https://git.kernel.org/pub/scm/linux/kernel/git/daniel.lezcano/linux.git//9fea79f5948bd5e0bea1cf773fd7742a6855017b Author: Herve Codina (Schneider Electric) <herve.codina@bootlin.com> AuthorDate: Thu, 01 Oct 2026 10:22:21 +02:00 Committer: Daniel Lezcano <daniel.lezcano@kernel.org> CommitterDate: Fri, 02 Oct 2026 11:42:32 +02:00 dt-bindings: timer: Add the Renesas RZ/N1 timer 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> Signed-off-by: Daniel Lezcano <daniel.lezcano@kernel.org> 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> Link: https://patch.msgid.link/20261001082225.125956-2-herve.codina@bootlin.com --- Documentation/devicetree/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 0000000..7218c0c --- /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>; + }; +... ^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v7 2/4] clocksource/drivers: Add support for the Renesas RZ/N1 timers 2026-10-01 8:22 [PATCH v7 0/4] timers: Add support for RZ/N1 SoCs timers Herve Codina (Schneider Electric) 2026-10-01 8:22 ` [PATCH v7 1/4] dt-bindings: timer: Add the Renesas RZ/N1 timer Herve Codina (Schneider Electric) @ 2026-10-01 8:22 ` Herve Codina (Schneider Electric) 2026-10-14 20:20 ` [clocksource: timers/drivers/next] " clocksource-bot for Herve Codina (Schneider Electric) 2026-10-01 8:22 ` [PATCH v7 3/4] ARM: dts: r9a06g032: Add support for timers Herve Codina (Schneider Electric) ` (2 subsequent siblings) 4 siblings, 1 reply; 10+ messages in thread From: Herve Codina (Schneider Electric) @ 2026-10-01 8:22 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 | 483 +++++++++++++++++++++++++++++++ 3 files changed, 495 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..25ebebff3c63 --- /dev/null +++ b/drivers/clocksource/timer-rzn1.c @@ -0,0 +1,483 @@ +// 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); + + if (timer->ced.event_handler) + 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 [flat|nested] 10+ messages in thread
* [clocksource: timers/drivers/next] clocksource/drivers: Add support for the Renesas RZ/N1 timers 2026-10-01 8:22 ` [PATCH v7 2/4] clocksource/drivers: Add support for the Renesas RZ/N1 timers Herve Codina (Schneider Electric) @ 2026-10-14 20:20 ` clocksource-bot for Herve Codina (Schneider Electric) 0 siblings, 0 replies; 10+ messages in thread From: clocksource-bot for Herve Codina (Schneider Electric) @ 2026-10-14 20:20 UTC (permalink / raw) To: linux-kernel Cc: Herve Codina (Schneider Electric), Daniel Lezcano, Wolfram Sang, daniel.lezcano The following commit has been merged into the timers/drivers/next branch of clocksource: Commit-ID: 435167b4f30191aa7368de68870117bc7218ee8f Gitweb: https://git.kernel.org/pub/scm/linux/kernel/git/daniel.lezcano/linux.git//435167b4f30191aa7368de68870117bc7218ee8f Author: Herve Codina (Schneider Electric) <herve.codina@bootlin.com> AuthorDate: Thu, 01 Oct 2026 10:22:22 +02:00 Committer: Daniel Lezcano <daniel.lezcano@kernel.org> CommitterDate: Fri, 02 Oct 2026 11:42:40 +02:00 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 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> Signed-off-by: Daniel Lezcano <daniel.lezcano@kernel.org> Tested-by: Wolfram Sang <wsa+renesas@sang-engineering.com> Link: https://patch.msgid.link/20261001082225.125956-3-herve.codina@bootlin.com --- drivers/clocksource/Kconfig | 11 +- drivers/clocksource/Makefile | 1 +- drivers/clocksource/timer-rzn1.c | 483 ++++++++++++++++++++++++++++++- 3 files changed, 495 insertions(+) create mode 100644 drivers/clocksource/timer-rzn1.c diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig index d9c76dd..da83483 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 b46376a..464ec66 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 0000000..25ebebf --- /dev/null +++ b/drivers/clocksource/timer-rzn1.c @@ -0,0 +1,483 @@ +// 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); + + if (timer->ced.event_handler) + 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); ^ permalink raw reply [flat|nested] 10+ messages in thread
* [PATCH v7 3/4] ARM: dts: r9a06g032: Add support for timers 2026-10-01 8:22 [PATCH v7 0/4] timers: Add support for RZ/N1 SoCs timers Herve Codina (Schneider Electric) 2026-10-01 8:22 ` [PATCH v7 1/4] dt-bindings: timer: Add the Renesas RZ/N1 timer Herve Codina (Schneider Electric) 2026-10-01 8:22 ` [PATCH v7 2/4] clocksource/drivers: Add support for the Renesas RZ/N1 timers Herve Codina (Schneider Electric) @ 2026-10-01 8:22 ` Herve Codina (Schneider Electric) 2026-10-01 8:22 ` [PATCH v7 4/4] MAINTAINERS: Add the Renesas RZ/N1 timers driver entry Herve Codina (Schneider Electric) 2026-10-02 9:49 ` [PATCH v7 0/4] timers: Add support for RZ/N1 SoCs timers Daniel Lezcano 4 siblings, 0 replies; 10+ messages in thread From: Herve Codina (Schneider Electric) @ 2026-10-01 8:22 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 [flat|nested] 10+ messages in thread
* [PATCH v7 4/4] MAINTAINERS: Add the Renesas RZ/N1 timers driver entry 2026-10-01 8:22 [PATCH v7 0/4] timers: Add support for RZ/N1 SoCs timers Herve Codina (Schneider Electric) ` (2 preceding siblings ...) 2026-10-01 8:22 ` [PATCH v7 3/4] ARM: dts: r9a06g032: Add support for timers Herve Codina (Schneider Electric) @ 2026-10-01 8:22 ` Herve Codina (Schneider Electric) 2026-10-14 20:20 ` [clocksource: timers/drivers/next] " clocksource-bot for Herve Codina (Schneider Electric) 2026-10-02 9:49 ` [PATCH v7 0/4] timers: Add support for RZ/N1 SoCs timers Daniel Lezcano 4 siblings, 1 reply; 10+ messages in thread From: Herve Codina (Schneider Electric) @ 2026-10-01 8:22 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 [flat|nested] 10+ messages in thread
* [clocksource: timers/drivers/next] MAINTAINERS: Add the Renesas RZ/N1 timers driver entry 2026-10-01 8:22 ` [PATCH v7 4/4] MAINTAINERS: Add the Renesas RZ/N1 timers driver entry Herve Codina (Schneider Electric) @ 2026-10-14 20:20 ` clocksource-bot for Herve Codina (Schneider Electric) 0 siblings, 0 replies; 10+ messages in thread From: clocksource-bot for Herve Codina (Schneider Electric) @ 2026-10-14 20:20 UTC (permalink / raw) To: linux-kernel Cc: Herve Codina (Schneider Electric), Daniel Lezcano, Geert Uytterhoeven, Wolfram Sang, daniel.lezcano The following commit has been merged into the timers/drivers/next branch of clocksource: Commit-ID: 57fb7ac3584c27939a55eb3870be9d46f0b7e7e9 Gitweb: https://git.kernel.org/pub/scm/linux/kernel/git/daniel.lezcano/linux.git//57fb7ac3584c27939a55eb3870be9d46f0b7e7e9 Author: Herve Codina (Schneider Electric) <herve.codina@bootlin.com> AuthorDate: Thu, 01 Oct 2026 10:22:24 +02:00 Committer: Daniel Lezcano <daniel.lezcano@kernel.org> CommitterDate: Fri, 02 Oct 2026 11:42:48 +02:00 MAINTAINERS: Add the Renesas RZ/N1 timers driver entry 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> Signed-off-by: Daniel Lezcano <daniel.lezcano@kernel.org> Reviewed-by: Geert Uytterhoeven <geert+renesas@glider.be> Reviewed-by: Wolfram Sang <wsa+renesas@sang-engineering.com> Link: https://patch.msgid.link/20261001082225.125956-5-herve.codina@bootlin.com --- MAINTAINERS | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/MAINTAINERS b/MAINTAINERS index 72294dd..7bdacc8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -23295,6 +23295,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 ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v7 0/4] timers: Add support for RZ/N1 SoCs timers 2026-10-01 8:22 [PATCH v7 0/4] timers: Add support for RZ/N1 SoCs timers Herve Codina (Schneider Electric) ` (3 preceding siblings ...) 2026-10-01 8:22 ` [PATCH v7 4/4] MAINTAINERS: Add the Renesas RZ/N1 timers driver entry Herve Codina (Schneider Electric) @ 2026-10-02 9:49 ` Daniel Lezcano 2026-10-12 9:16 ` Herve Codina 4 siblings, 1 reply; 10+ messages in thread From: Daniel Lezcano @ 2026-10-02 9:49 UTC (permalink / raw) To: Herve Codina (Schneider Electric), Wolfram Sang, 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 On 10/1/26 10:22, Herve Codina (Schneider Electric) wrote: > 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, Applied, patch 1,2 and 4 ^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v7 0/4] timers: Add support for RZ/N1 SoCs timers 2026-10-02 9:49 ` [PATCH v7 0/4] timers: Add support for RZ/N1 SoCs timers Daniel Lezcano @ 2026-10-12 9:16 ` Herve Codina 0 siblings, 0 replies; 10+ messages in thread From: Herve Codina @ 2026-10-12 9:16 UTC (permalink / raw) To: Geert Uytterhoeven Cc: Daniel Lezcano, Wolfram Sang, Daniel Lezcano, Thomas Gleixner, Rob Herring, Krzysztof Kozlowski, Conor Dooley, Magnus Damm, linux-renesas-soc, linux-kernel, devicetree, Pascal Eberhard, Miquel Raynal, Thomas Petazzoni Hi Geert, On Fri, 2 Oct 2026 11:49:49 +0200 Daniel Lezcano <daniel.lezcano@oss.qualcomm.com> wrote: > On 10/1/26 10:22, Herve Codina (Schneider Electric) wrote: > > 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, > > Applied, patch 1,2 and 4 > Daniel applied almost all patches of the series. The only missing one is patch 3 which has to be applied on your tree. IMHO, we have the green light to apply it. Can you consider it? Best regards, Hervé ^ permalink raw reply [flat|nested] 10+ messages in thread
end of thread, other threads:[~2026-10-14 20:21 UTC | newest] Thread overview: 10+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-10-01 8:22 [PATCH v7 0/4] timers: Add support for RZ/N1 SoCs timers Herve Codina (Schneider Electric) 2026-10-01 8:22 ` [PATCH v7 1/4] dt-bindings: timer: Add the Renesas RZ/N1 timer Herve Codina (Schneider Electric) 2026-10-14 20:20 ` [clocksource: timers/drivers/next] " clocksource-bot for Herve Codina (Schneider Electric) 2026-10-01 8:22 ` [PATCH v7 2/4] clocksource/drivers: Add support for the Renesas RZ/N1 timers Herve Codina (Schneider Electric) 2026-10-14 20:20 ` [clocksource: timers/drivers/next] " clocksource-bot for Herve Codina (Schneider Electric) 2026-10-01 8:22 ` [PATCH v7 3/4] ARM: dts: r9a06g032: Add support for timers Herve Codina (Schneider Electric) 2026-10-01 8:22 ` [PATCH v7 4/4] MAINTAINERS: Add the Renesas RZ/N1 timers driver entry Herve Codina (Schneider Electric) 2026-10-14 20:20 ` [clocksource: timers/drivers/next] " clocksource-bot for Herve Codina (Schneider Electric) 2026-10-02 9:49 ` [PATCH v7 0/4] timers: Add support for RZ/N1 SoCs timers Daniel Lezcano 2026-10-12 9:16 ` Herve Codina
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®