[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-ID: <dd34e5f4-5027-4096-9f32-129c8a067d0a@de.bosch.com>
Date: Tue, 22 Jul 2025 13:41:46 +0200
From: Dirk Behme <dirk.behme@...bosch.com>
To: Daniel Almeida <daniel.almeida@...labora.com>, Miguel Ojeda
<ojeda@...nel.org>, Alex Gaynor <alex.gaynor@...il.com>, Boqun Feng
<boqun.feng@...il.com>, Gary Guo <gary@...yguo.net>,
Björn Roy Baron <bjorn3_gh@...tonmail.com>, Andreas Hindborg
<a.hindborg@...nel.org>, Alice Ryhl <aliceryhl@...gle.com>, Trevor Gross
<tmgross@...ch.edu>, Danilo Krummrich <dakr@...nel.org>, Greg Kroah-Hartman
<gregkh@...uxfoundation.org>, "Rafael J. Wysocki" <rafael@...nel.org>, Thomas
Gleixner <tglx@...utronix.de>, Bjorn Helgaas <bhelgaas@...gle.com>,
Krzysztof Wilczyński <kwilczynski@...nel.org>, Benno Lossin
<lossin@...nel.org>
CC: <linux-kernel@...r.kernel.org>, <rust-for-linux@...r.kernel.org>,
<linux-pci@...r.kernel.org>, Geert Uytterhoeven <geert+renesas@...der.be>,
Wolfram Sang <wsa+renesas@...g-engineering.com>
Subject: Re: [PATCH v7 0/6] rust: add support for request_irq
On 15/07/2025 17:16, Daniel Almeida wrote:
> Changes in v7:
> - Rebased on top of driver-core-next
> - Added Flags::new(), which is a const fn. This lets us use build_assert!()
> to verify the casts (hopefully this is what you meant, Alice?)
> - Changed the Flags inner type to take c_ulong directly, to minimize casts
> (Thanks, Alice)
> - Moved the flag constants into Impl Flags, instead of using a separate
> module (Alice)
> - Reverted to using #[repr(u32)] in Threaded/IrqReturn (Thanks Alice,
> Benno)
> - Fixed all instances where the full path was specified for types in the
> prelude (Alice)
> - Removed 'static from the CStr used to perform the lookup in the platform
> accessor (Alice)
> - Renamed the PCI accessors, as asked by Danilo
> - Added more docs to Flags, going into more detail on what they do and how
> to use them (Miguel)
> - Fixed the indentation in some of the docs (Alice)
> - Added Alice's r-b as appropriate
> - Link to v6: https://lore.kernel.org/rust-for-linux/20250703-topics-tyr-request_irq-v6-0-74103bdc7c52@collabora.com/
Looking for an easy way to test interrupts on an ARM64 Renesas RCar3 SoC
I found a quite simple timer unit (TMU) which has a configurable (start
value & frequency) count down. An interrupt is generated when the
counter reaches 0. And the counter restarts then. There is a C driver
for this already [1].
Using this patch series together with Alice's [2] I got a quite simple
periodic 1 min interrupt handling to run (just for testing, of course
not a full driver): [3] (output [4]).
With that:
Tested-by: Dirk Behme <dirk.behme@...bosch.com>
Thanks to Daniel for the support!
Dirk
[1]
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/tree/drivers/clocksource/sh_tmu.c
[2]
https://lore.kernel.org/rust-for-linux/20250721-irq-bound-device-v1-1-4fb2af418a63@google.com/
[3]
diff --git a/drivers/clocksource/Kconfig b/drivers/clocksource/Kconfig
index 645f517a1ac2..d009a0e3508c 100644
--- a/drivers/clocksource/Kconfig
+++ b/drivers/clocksource/Kconfig
@@ -554,7 +554,16 @@ config RENESAS_OSTM
Enables the support for the Renesas OSTM.
config SH_TIMER_TMU
- bool "Renesas TMU timer driver" if COMPILE_TEST
+ bool "Renesas TMU timer driver"
+ depends on HAS_IOMEM
+ default SYS_SUPPORTS_SH_TMU
+ help
+ This enables build of a clocksource and clockevent driver for
+ the 32-bit Timer Unit (TMU) hardware available on a wide range
+ SoCs from Renesas.
+
+config SH_TIMER_TMU_RUST
+ bool "Renesas TMU Rust timer driver"
depends on HAS_IOMEM
default SYS_SUPPORTS_SH_TMU
help
diff --git a/drivers/clocksource/Makefile b/drivers/clocksource/Makefile
index 205bf3b0a8f3..66567f871502 100644
--- a/drivers/clocksource/Makefile
+++ b/drivers/clocksource/Makefile
@@ -12,6 +12,7 @@ obj-$(CONFIG_SH_TIMER_CMT) += sh_cmt.o
obj-$(CONFIG_SH_TIMER_MTU2) += sh_mtu2.o
obj-$(CONFIG_RENESAS_OSTM) += renesas-ostm.o
obj-$(CONFIG_SH_TIMER_TMU) += sh_tmu.o
+obj-$(CONFIG_SH_TIMER_TMU_RUST) += sh_tmu_rust.o
obj-$(CONFIG_EM_TIMER_STI) += em_sti.o
obj-$(CONFIG_CLKBLD_I8253) += i8253.o
obj-$(CONFIG_CLKSRC_MMIO) += mmio.o
diff --git a/drivers/clocksource/sh_tmu_rust.rs
b/drivers/clocksource/sh_tmu_rust.rs
new file mode 100644
index 000000000000..328f9541d1bb
--- /dev/null
+++ b/drivers/clocksource/sh_tmu_rust.rs
@@ -0,0 +1,113 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Rust Renesas TMU driver.
+
+use kernel::{
+ c_str,
+ device::{Core, Device, Bound},
+ devres::Devres,
+ io::mem::IoMem,
+ irq::{flags::Flags, IrqReturn, Registration},
+ of, platform,
+ prelude::*,
+ sync::Arc,
+ types::ARef,
+};
+
+struct RenesasTMUDriver {
+ pdev: ARef<platform::Device>,
+ _registration: Arc<Registration<Handler>>,
+ _iomem: Arc<Devres<IoMem>>,
+}
+
+struct Info;
+
+kernel::of_device_table!(
+ OF_TABLE,
+ MODULE_OF_TABLE,
+ <RenesasTMUDriver as platform::Driver>::IdInfo,
+ [(of::DeviceId::new(c_str!("renesas,tmu")), Info)]
+);
+
+const TSTR: usize = 0x4; // 8 Bit register
+const TCOR: usize = 0x8; // 32 Bit register
+const TCNT: usize = 0xC; // 32 Bit register
+const TCR: usize = 0x10; // 16 Bit register
+
+struct Handler {
+ iomem: Arc<Devres<IoMem>>,
+}
+
+impl kernel::irq::request::Handler for Handler {
+ fn handle(&self, dev: &Device<Bound>) -> IrqReturn {
+ pr_info!("Renesas TMU IRQ handler called.\n");
+
+ // Reset the underflow flag
+ let io = self.iomem.access(dev).unwrap();
+ let tcr = io.try_read16_relaxed(TCR).unwrap_or(0);
+ if tcr & (0x1 << 8) != 0 {
+ io.try_write16_relaxed(tcr & !(0x1 << 8), TCR).unwrap_or(());
+ }
+
+ IrqReturn::Handled
+ }
+}
+
+impl platform::Driver for RenesasTMUDriver {
+ type IdInfo = Info;
+ const OF_ID_TABLE: Option<of::IdTable<Self::IdInfo>> = Some(&OF_TABLE);
+
+ fn probe(
+ pdev: &platform::Device<Core>,
+ _info: Option<&Self::IdInfo>,
+ ) -> Result<Pin<KBox<Self>>> {
+ let dev = pdev.as_ref();
+
+ dev_dbg!(dev, "Probe Rust Renesas TMU driver.\n");
+
+ let request = pdev.request_io_by_index(0).ok_or(EINVAL)?;
+ let iomem = Arc::pin_init(request.iomap()?, GFP_KERNEL)?;
+ let io = Arc::pin_init(iomem.access(dev)?, GFP_KERNEL)?;
+
+ // Set count to 1 minute. Clock is 16.66MHz / 4 = 4.165MHz
+ let timeout = 4165000 * 60; // 1 minute in clock ticks
+ io.try_write32_relaxed(timeout, TCOR)?;
+ io.try_write32_relaxed(timeout, TCNT)?;
+ // Enable underflow interrupt (UNIE, Underflow Interrupt Control)
+ let tcr = io.try_read16_relaxed(TCR)?;
+ io.try_write16_relaxed(tcr | 0x1 << 5, TCR)?;
+
+ let request = pdev.irq_by_index(0)?;
+ dev_info!(dev, "IRQ: {}\n", request.irq());
+ let registration = Registration::new(request, Flags::SHARED,
c_str!("tmu"), Handler{iomem: iomem.clone()});
+ let registration = Arc::pin_init(registration, GFP_KERNEL)?;
+
+ // Enable TMU
+ io.try_write8_relaxed(0x1, TSTR)?;
+ // Read back registers to verify
+ dev_info!(dev, "TSTR: 0x{:x}\n", io.try_read8_relaxed(TSTR)?);
+ dev_info!(dev, "TCOR: 0x{:x}\n", io.try_read32_relaxed(TCOR)?);
+ dev_info!(dev, "TCNT: 0x{:x}\n", io.try_read32_relaxed(TCNT)?);
+ dev_info!(dev, "TCR: 0x{:x}\n", io.try_read16_relaxed(TCR)?);
+
+ let drvdata = KBox::pin_init(Self { pdev: pdev.into(),
_registration: registration, _iomem: iomem.clone()}, GFP_KERNEL)?;
+
+ dev_info!(dev, "probe done\n");
+
+ Ok(drvdata)
+ }
+}
+
+impl Drop for RenesasTMUDriver {
+ fn drop(&mut self) {
+ dev_dbg!(self.pdev.as_ref(), "Remove Rust Renesas TMU driver.\n");
+ }
+}
+
+kernel::module_platform_driver! {
+ type: RenesasTMUDriver,
+ name: "rust_tmu",
+ authors: ["Dirk Behme"],
+ description: "Rust Renesas TMU driver",
+ license: "GPL v2",
+}
[4] Interrupt each 60s:
...
[ 430.655055] rust_tmu: Renesas TMU IRQ handler called.
[ 490.637054] rust_tmu: Renesas TMU IRQ handler called.
[ 550.619052] rust_tmu: Renesas TMU IRQ handler called.
[ 610.601050] rust_tmu: Renesas TMU IRQ handler called.
[ 670.583049] rust_tmu: Renesas TMU IRQ handler called.
[ 730.565047] rust_tmu: Renesas TMU IRQ handler called.
...
Powered by blists - more mailing lists