@@ -23,3 +23,7 @@
&uart1 {
status = "okay";
};
+
+&timer0 {
+ status = "okay";
+};
@@ -33,5 +33,19 @@
clock = <150000000>; /* CLK_PERI = clk_sys 150MHz */
status = "disabled";
};
+
+ timer0: timer@400b0000 {
+ compatible = "raspberrypi,rp2350-timer";
+ reg = <0x400b0000 0x1000>;
+ clock-frequency = <1000000>; /* 1MHz microsecond tick */
+ status = "disabled";
+ };
+
+ timer1: timer@400b8000 {
+ compatible = "raspberrypi,rp2350-timer";
+ reg = <0x400b8000 0x1000>;
+ clock-frequency = <1000000>; /* 1MHz microsecond tick */
+ status = "disabled";
+ };
};
};
@@ -119,11 +119,13 @@ struct rp2350_resets_regs {
#define RP2350_RESETS ((struct rp2350_resets_regs *)RP2350_RESETS_BASE)
-/* Reset bits for peripherals we care about */
+/* Reset bits for peripherals */
#define RESETS_RESET_UART0 BIT(26)
#define RESETS_RESET_UART1 BIT(27)
#define RESETS_RESET_PLL_SYS BIT(14)
#define RESETS_RESET_PLL_USB BIT(15)
+#define RESETS_RESET_TIMER0 BIT(23)
+#define RESETS_RESET_TIMER1 BIT(24)
/*
* XOSC (Crystal Oscillator) register map
@@ -330,6 +332,18 @@ struct rp2350_uart_regs {
#define RP2350_UART_IBRD_115200 81
#define RP2350_UART_FBRD_115200 24
+/*
+ * Tick generator - see Datasheet section 8.5.1
+ * Each consumer (TIMER0/1, watchdog, etc) has its own CTRL/CYCLES/COUNT
+ * triplet. The generators are clocked from clk_ref, so a CYCLES divisor of
+ * (clk_ref_hz / 1MHz) produces the 1MHz (1us) tick the timers expect.
+ */
+#define RP2350_TICKS_TIMER0_CTRL 0x18
+#define RP2350_TICKS_TIMER0_CYCLES 0x1c
+#define RP2350_TICKS_TIMER1_CTRL 0x24
+#define RP2350_TICKS_TIMER1_CYCLES 0x28
+#define TICKS_CTRL_ENABLE BIT(0)
+
/*
* Atomic register access
* RP2350 (like RP2040) provides atomic set/clear/xor aliases
@@ -346,6 +360,7 @@ struct rp2350_uart_regs {
void rp2350_soc_init(void);
void rp2350_uart_early_init(void);
+void rp2350_timer_init(void);
#endif /* ! __ASSEMBLY__ */
@@ -190,6 +190,35 @@ void rp2350_uart_early_init(void)
writel(UART_CR_UARTEN | UART_CR_TXE | UART_CR_RXE, &uart->cr);
}
+/*
+ * rp2350_timer_init() - Enable the TIMER0/TIMER1 microsecond timer
+ *
+ * The RP2350 64-bit system timer needs an external 1MHz tick. Unlike RP2040,
+ * the RP2350 does not auto-start this tick, so the TICKS block must be
+ * programmed here. The tick is driven from clk_ref (XOSC = 12MHz), so a
+ * CYCLES divisor of 12 yields a 1MHz (1us) tick. See datasheet section 12.8.1
+ */
+void rp2350_timer_init(void)
+{
+ struct rp2350_resets_regs *resets = RP2350_RESETS;
+
+ /* Release TIMER0 from reset */
+ rp2350_reg_clr(&resets->reset, RESETS_RESET_TIMER0);
+ rp2350_wait_reset_done(RESETS_RESET_TIMER0);
+
+ /* Release TIMER1 from reset */
+ rp2350_reg_clr(&resets->reset, RESETS_RESET_TIMER1);
+ rp2350_wait_reset_done(RESETS_RESET_TIMER1);
+
+ /* Start the TIMER0 tick generator at 1MHz */
+ writel(RP2350_XOSC_HZ / 1000000, RP2350_TICKS_BASE + RP2350_TICKS_TIMER0_CYCLES);
+ writel(TICKS_CTRL_ENABLE, RP2350_TICKS_BASE + RP2350_TICKS_TIMER0_CTRL);
+
+ /* Start the TIMER1 tick generator at 1MHz */
+ writel(RP2350_XOSC_HZ / 1000000, RP2350_TICKS_BASE + RP2350_TICKS_TIMER1_CYCLES);
+ writel(TICKS_CTRL_ENABLE, RP2350_TICKS_BASE + RP2350_TICKS_TIMER1_CTRL);
+}
+
/*
* rp2350_soc_init() - Full SoC initialization sequence
*
@@ -210,4 +239,6 @@ void rp2350_soc_init(void)
rp2350_clocks_init();
/* Now init UART - it will source clk_peri from clk_sys */
rp2350_uart_early_init();
+ /* Start the 1MHz microsecond timer (TIMER0) */
+ rp2350_timer_init();
}
@@ -27,3 +27,4 @@ CONFIG_SPECIFY_CONSOLE_INDEX=y
CONFIG_DM_SERIAL=y
CONFIG_PL01X_SERIAL=y
CONFIG_TIMER=y
+CONFIG_RP2350_TIMER=y
@@ -348,4 +348,12 @@ config GOLDFISH_TIMER
It uses the Goldfish RTC hardware to provide a nanosecond-resolution
timer, commonly found in QEMU virt machines.
+config RP2350_TIMER
+ bool "Raspberry Pi RP2350 timer support"
+ depends on TIMER
+ help
+ Select this to enable support for the 64-bit microsecond timers
+ found on the Raspberry Pi RP2350 SoC. The timer is fed a 1MHz tick
+ by the SoC tick generator.
+
endmenu
@@ -37,3 +37,4 @@ obj-$(CONFIG_IMX_GPT_TIMER) += imx-gpt-timer.o
obj-$(CONFIG_XILINX_TIMER) += xilinx-timer.o
obj-$(CONFIG_STARFIVE_TIMER) += starfive-timer.o
obj-$(CONFIG_GOLDFISH_TIMER) += goldfish_timer.o
+obj-$(CONFIG_RP2350_TIMER) += rp2350_timer.o
new file mode 100644
@@ -0,0 +1,62 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Timer driver for the Raspberry Pi RP2350 64-bit microsecond timers
+ *
+ * Copyright (C) 2026 Bhargav Das <bhargavdas100@gmail.com>
+ */
+
+#include <dm.h>
+#include <timer.h>
+#include <asm/io.h>
+
+#define RP2350_TIMER_TIMEHR 0x08 /* Latched high 32 bits of time */
+#define RP2350_TIMER_TIMELR 0x0c /* Latched low 32 bits of time */
+
+struct rp2350_timer_priv {
+ void __iomem *base;
+};
+
+static u64 rp2350_timer_get_count(struct udevice *dev)
+{
+ struct rp2350_timer_priv *priv = dev_get_priv(dev);
+ u32 hi, lo;
+
+ /*
+ * Reading TIMELR latches the current value of TIMEHR, so the low
+ * word must always be read before the high word to obtain a coherent
+ * 64-bit value.
+ */
+ lo = readl(priv->base + RP2350_TIMER_TIMELR);
+ hi = readl(priv->base + RP2350_TIMER_TIMEHR);
+
+ return ((u64)hi << 32) | lo;
+}
+
+static int rp2350_timer_probe(struct udevice *dev)
+{
+ struct rp2350_timer_priv *priv = dev_get_priv(dev);
+
+ priv->base = dev_read_addr_ptr(dev);
+ if (!priv->base)
+ return -EINVAL;
+
+ return 0;
+}
+
+static const struct timer_ops rp2350_timer_ops = {
+ .get_count = rp2350_timer_get_count,
+};
+
+static const struct udevice_id rp2350_timer_ids[] = {
+ { .compatible = "raspberrypi,rp2350-timer" },
+ { }
+};
+
+U_BOOT_DRIVER(rp2350_timer) = {
+ .name = "rp2350_timer",
+ .id = UCLASS_TIMER,
+ .of_match = rp2350_timer_ids,
+ .priv_auto = sizeof(struct rp2350_timer_priv),
+ .probe = rp2350_timer_probe,
+ .ops = &rp2350_timer_ops,
+};
Implement a Driver Model (DM) timer driver for the RP2350 SoC. Previously, CONFIG_TIMER was enabled purely to satisfy compilation and linker requirements. Because the platform relied on generic fallback definitions, invoking the `timer` command or any timing-related operation (such as delays or timeouts) caused the system to crash. The early system tick generation is initialized in mach-rp2xxx/rp2350.c before the main driver probes. Both timer0 and timer1 have been verified to work correctly. Enabled timer0 by default in the device tree. Signed-off-by: Bhargav Das <bhargavdas100@gmail.com> --- arch/arm/dts/rp2350-pico2.dts | 4 ++ arch/arm/dts/rp2350.dtsi | 14 +++++ arch/arm/include/asm/arch-rp2xxx/rp2350.h | 17 ++++++- arch/arm/mach-rp2xxx/rp2350.c | 31 ++++++++++++ configs/rpi_pico2_defconfig | 1 + drivers/timer/Kconfig | 8 +++ drivers/timer/Makefile | 1 + drivers/timer/rp2350_timer.c | 62 +++++++++++++++++++++++ 8 files changed, 137 insertions(+), 1 deletion(-) create mode 100644 drivers/timer/rp2350_timer.c