public inbox for rust-for-linux@vger.kernel.org
 help / color / mirror / Atom feed
From: Ke Sun <sk.alvin.x@gmail.com>
To: "Ke Sun" <sunke@kylinos.cn>,
	"Alexandre Belloni" <alexandre.belloni@bootlin.com>,
	"Miguel Ojeda" <ojeda@kernel.org>,
	"Boqun Feng" <boqun.feng@gmail.com>,
	"Gary Guo" <gary@garyguo.net>,
	"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
	"Benno Lossin" <lossin@kernel.org>,
	"Andreas Hindborg" <a.hindborg@kernel.org>,
	"Alice Ryhl" <aliceryhl@google.com>,
	"Trevor Gross" <tmgross@umich.edu>,
	"Danilo Krummrich" <dakr@kernel.org>
Cc: linux-rtc@vger.kernel.org, rust-for-linux@vger.kernel.org
Subject: Re: [RFC PATCH v3 5/5] rust: add PL031 RTC driver
Date: Mon, 19 Jan 2026 17:12:23 +0800	[thread overview]
Message-ID: <54d8007e-c54e-44a2-8cfe-df10495cda80@gmail.com> (raw)
In-Reply-To: <20260116163401.312002-1-sunke@kylinos.cn>

This driver has been tested on QEMU with the following command:
qemu-system-aarch64 -machine virt,virtualization=on -cpu max -smp 2 -m 4g \
   -serial mon:stdio -kernel arch/arm64/boot/Image  -hda 
PATHTO/virtualdisk.qcow2 \
   -append "root=/dev/vdaX no_console_suspend loglevel=8 console=ttyAMA0" \
   -display none -nic user,hostfwd=tcp::10022-:22

Test Results:

Driver Registration:
    [    1.886444][    T1] rtc-pl031-rust 9010000.pl031: registered as rtc0
    [    1.888070][    T1] rtc-pl031-rust 9010000.pl031: setting system 
clock to 2026-01-19T08:49:36 UTC (1768812576)

Interrupt Registration:
    # cat /proc/interrupts |grep pl031
     21:          0          0    GICv2  34 Level     rtc-pl031

RTC Time Reading:
    # hwclock -r
    2026-01-19 09:03:24.961787+08:00

Suspend/Resume with Alarm:
    # rtcwake --mode mem --seconds 5
    rtcwake: wakeup from "mem" using /dev/rtc0 at Mon Jan 19 09:04:13 2026
    [  242.699314][ T2832] PM: suspend entry (s2idle)
    [  242.717296][   T12] Filesystems sync: 0.009 seconds
    [  242.741471][ T2832] Freezing user space processes
    [  242.761472][ T2832] Freezing user space processes completed 
(elapsed 0.019 seconds)
    [  242.763281][ T2832] OOM killer disabled.
    [  242.763681][ T2832] Freezing remaining freezable tasks
    [  242.766629][ T2832] Freezing remaining freezable tasks completed 
(elapsed 0.002 seconds)
    [  248.678304][   T82] virtio_blk virtio1: 2/0/0 default/read/poll 
queues
    [  248.692577][ T2832] OOM killer enabled.
    [  248.692724][ T2832] Restarting tasks: Starting
    [  248.744504][ T2832] Restarting tasks: Done
    [  248.745530][ T2832] random: crng reseeded on system resumption
    [  248.746749][ T2832] PM: suspend exit

Driver Rebind:
    /sys/bus/amba/drivers/rtc-pl031-rust# echo 9010000.pl031 > unbind
    /sys/bus/amba/drivers/rtc-pl031-rust# echo 9010000.pl031 > bind
    [  334.060940][ T1837] rtc-pl031-rust 9010000.pl031: registered as rtc1

All tests passed successfully.

On 1/17/26 00:34, Ke Sun wrote:
> Add Rust implementation of the PL031 RTC driver.
>
> Signed-off-by: Ke Sun <sunke@kylinos.cn>
> ---
>   drivers/rtc/Kconfig           |   9 +
>   drivers/rtc/Makefile          |   1 +
>   drivers/rtc/rtc_pl031_rust.rs | 513 ++++++++++++++++++++++++++++++++++
>   3 files changed, 523 insertions(+)
>   create mode 100644 drivers/rtc/rtc_pl031_rust.rs
>
> diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
> index 50dc779f7f983..137cea1824edd 100644
> --- a/drivers/rtc/Kconfig
> +++ b/drivers/rtc/Kconfig
> @@ -1591,6 +1591,15 @@ config RTC_DRV_PL031
>   	  To compile this driver as a module, choose M here: the
>   	  module will be called rtc-pl031.
>   
> +config RTC_DRV_PL031_RUST
> +	tristate "ARM AMBA PL031 RTC (Rust)"
> +	depends on RUST && RTC_CLASS
> +	help
> +	  This is the Rust implementation of the PL031 RTC driver.
> +	  It provides the same functionality as the C driver but is
> +	  written in Rust for improved memory safety. The driver supports
> +	  ARM, ST v1, and ST v2 variants of the PL031 RTC controller.
> +
>   config RTC_DRV_AT91RM9200
>   	tristate "AT91RM9200 or some AT91SAM9 RTC"
>   	depends on ARCH_AT91 || COMPILE_TEST
> diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
> index 6cf7e066314e1..10f540e7409b4 100644
> --- a/drivers/rtc/Makefile
> +++ b/drivers/rtc/Makefile
> @@ -139,6 +139,7 @@ obj-$(CONFIG_RTC_DRV_PCF8583)	+= rtc-pcf8583.o
>   obj-$(CONFIG_RTC_DRV_PIC32)	+= rtc-pic32.o
>   obj-$(CONFIG_RTC_DRV_PL030)	+= rtc-pl030.o
>   obj-$(CONFIG_RTC_DRV_PL031)	+= rtc-pl031.o
> +obj-$(CONFIG_RTC_DRV_PL031_RUST)	+= rtc_pl031_rust.o
>   obj-$(CONFIG_RTC_DRV_PM8XXX)	+= rtc-pm8xxx.o
>   obj-$(CONFIG_RTC_DRV_POLARFIRE_SOC)	+= rtc-mpfs.o
>   obj-$(CONFIG_RTC_DRV_PS3)	+= rtc-ps3.o
> diff --git a/drivers/rtc/rtc_pl031_rust.rs b/drivers/rtc/rtc_pl031_rust.rs
> new file mode 100644
> index 0000000000000..8ae48d96d1f94
> --- /dev/null
> +++ b/drivers/rtc/rtc_pl031_rust.rs
> @@ -0,0 +1,513 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later
> +//! Rust ARM AMBA PrimeCell 031 RTC driver
> +//!
> +//! This driver provides Real Time Clock functionality for ARM AMBA PrimeCell
> +//! 031 RTC controllers and their ST Microelectronics derivatives.
> +
> +use core::marker::PhantomPinned;
> +use kernel::{
> +    amba,
> +    bindings,
> +    c_str,
> +    device::{
> +        self,
> +        Core, //
> +    },
> +    devres::Devres,
> +    io::mem::IoMem,
> +    irq::{
> +        self,
> +        Handler,
> +        IrqReturn, //
> +    },
> +    prelude::*,
> +    rtc::{
> +        RtcDevice,
> +        RtcOps,
> +        RtcTime,
> +        RtcWkAlrm, //
> +    },
> +    sync::aref::ARef, //
> +};
> +
> +// Register offsets
> +const RTC_DR: usize = 0x00; // Data read register
> +const RTC_MR: usize = 0x04; // Match register
> +const RTC_LR: usize = 0x08; // Data load register
> +const RTC_CR: usize = 0x0c; // Control register
> +const RTC_IMSC: usize = 0x10; // Interrupt mask and set register
> +const RTC_RIS: usize = 0x14; // Raw interrupt status register
> +const RTC_MIS: usize = 0x18; // Masked interrupt status register
> +const RTC_ICR: usize = 0x1c; // Interrupt clear register
> +
> +// ST variants have additional timer functionality
> +#[allow(dead_code)]
> +const RTC_TDR: usize = 0x20; // Timer data read register
> +#[allow(dead_code)]
> +const RTC_TLR: usize = 0x24; // Timer data load register
> +#[allow(dead_code)]
> +const RTC_TCR: usize = 0x28; // Timer control register
> +const RTC_YDR: usize = 0x30; // Year data read register
> +const RTC_YMR: usize = 0x34; // Year match register
> +const RTC_YLR: usize = 0x38; // Year data load register
> +const PL031_REG_SIZE: usize = RTC_YLR + 4;
> +
> +// Control register bits
> +const RTC_CR_EN: u32 = 1 << 0; // Counter enable bit
> +const RTC_CR_CWEN: u32 = 1 << 26; // Clockwatch enable bit
> +
> +#[allow(dead_code)]
> +const RTC_TCR_EN: u32 = 1 << 1; // Periodic timer enable bit
> +
> +// Interrupt status and control register bits
> +const RTC_BIT_AI: u32 = 1 << 0; // Alarm interrupt bit
> +#[allow(dead_code)]
> +const RTC_BIT_PI: u32 = 1 << 1; // Periodic interrupt bit (ST variants only)
> +
> +// RTC event flags
> +#[allow(dead_code)]
> +const RTC_AF: u32 = bindings::RTC_AF;
> +#[allow(dead_code)]
> +const RTC_IRQF: u32 = bindings::RTC_IRQF;
> +
> +// ST v2 time format bit definitions
> +const RTC_SEC_SHIFT: u32 = 0;
> +const RTC_SEC_MASK: u32 = 0x3F << RTC_SEC_SHIFT; // Second [0-59]
> +const RTC_MIN_SHIFT: u32 = 6;
> +const RTC_MIN_MASK: u32 = 0x3F << RTC_MIN_SHIFT; // Minute [0-59]
> +const RTC_HOUR_SHIFT: u32 = 12;
> +const RTC_HOUR_MASK: u32 = 0x1F << RTC_HOUR_SHIFT; // Hour [0-23]
> +const RTC_WDAY_SHIFT: u32 = 17;
> +const RTC_WDAY_MASK: u32 = 0x7 << RTC_WDAY_SHIFT; // Day of Week [1-7] 1=Sunday
> +const RTC_MDAY_SHIFT: u32 = 20;
> +const RTC_MDAY_MASK: u32 = 0x1F << RTC_MDAY_SHIFT; // Day of Month [1-31]
> +const RTC_MON_SHIFT: u32 = 25;
> +const RTC_MON_MASK: u32 = 0xF << RTC_MON_SHIFT; // Month [1-12] 1=January
> +
> +/// Converts a binary value to BCD.
> +fn bin2bcd(val: u8) -> u8 {
> +    ((val / 10) << 4) | (val % 10)
> +}
> +
> +/// Converts a BCD value to binary.
> +fn bcd2bin(val: u8) -> u8 {
> +    ((val >> 4) * 10) + (val & 0x0F)
> +}
> +
> +/// Converts a Gregorian date to ST v2 RTC packed BCD format.
> +///
> +/// Returns a tuple of (packed_time, bcd_year) where packed_time contains
> +/// month, day, weekday, hour, minute, and second in a single 32-bit value.
> +fn stv2_tm_to_time(tm: &RtcTime) -> Result<(u32, u32)> {
> +    let year = tm.tm_year() + 1900;
> +    let mut wday = tm.tm_wday();
> +
> +    // Hardware wday masking doesn't work, so wday must be valid.
> +    if !(-1..=6).contains(&wday) {
> +        return Err(EINVAL);
> +    } else if wday == -1 {
> +        // wday is not provided, calculate it here.
> +        let time64 = tm.to_time64();
> +        let mut calc_tm = RtcTime::default();
> +        calc_tm.set_from_time64(time64);
> +        wday = calc_tm.tm_wday();
> +    }
> +
> +    // Convert year to BCD.
> +    let bcd_year =
> +        (u32::from(bin2bcd((year % 100) as u8))) | (u32::from(bin2bcd((year / 100) as u8)) << 8);
> +
> +    let st_time = ((tm.tm_mon() + 1) as u32) << RTC_MON_SHIFT
> +        | (tm.tm_mday() as u32) << RTC_MDAY_SHIFT
> +        | ((wday + 1) as u32) << RTC_WDAY_SHIFT
> +        | (tm.tm_hour() as u32) << RTC_HOUR_SHIFT
> +        | (tm.tm_min() as u32) << RTC_MIN_SHIFT
> +        | (tm.tm_sec() as u32) << RTC_SEC_SHIFT;
> +
> +    Ok((st_time, bcd_year))
> +}
> +
> +/// Converts ST v2 RTC packed BCD format to a Gregorian date.
> +///
> +/// Extracts time components from the packed 32-bit value and BCD year register,
> +/// then returns an RtcTime structure.
> +fn stv2_time_to_tm(st_time: u32, bcd_year: u32) -> RtcTime {
> +    let year_low = bcd2bin((bcd_year & 0xFF) as u8);
> +    let year_high = bcd2bin(((bcd_year >> 8) & 0xFF) as u8);
> +    let mut tm = RtcTime::default();
> +    tm.set_tm_year(i32::from(year_low) + i32::from(year_high) * 100);
> +    tm.set_tm_mon((((st_time & RTC_MON_MASK) >> RTC_MON_SHIFT) - 1) as i32);
> +    tm.set_tm_mday(((st_time & RTC_MDAY_MASK) >> RTC_MDAY_SHIFT) as i32);
> +    tm.set_tm_wday((((st_time & RTC_WDAY_MASK) >> RTC_WDAY_SHIFT) - 1) as i32);
> +    tm.set_tm_hour(((st_time & RTC_HOUR_MASK) >> RTC_HOUR_SHIFT) as i32);
> +    tm.set_tm_min(((st_time & RTC_MIN_MASK) >> RTC_MIN_SHIFT) as i32);
> +    tm.set_tm_sec(((st_time & RTC_SEC_MASK) >> RTC_SEC_SHIFT) as i32);
> +
> +    // Values are from valid RTC time structures and are non-negative.
> +    tm.set_tm_yday(tm.year_days());
> +    tm.set_tm_year(tm.tm_year() - 1900);
> +    tm
> +}
> +
> +/// Vendor-specific variant identifier for PL031 RTC controllers.
> +#[derive(Copy, Clone, PartialEq)]
> +enum VendorVariant {
> +    /// Original ARM version with 32-bit Unix timestamp format.
> +    Arm,
> +    /// First ST derivative with clockwatch mode and weekday support.
> +    StV1,
> +    /// Second ST derivative with packed BCD time format and year register.
> +    StV2,
> +}
> +
> +impl VendorVariant {
> +    fn clockwatch(&self) -> bool {
> +        matches!(self, VendorVariant::StV1 | VendorVariant::StV2)
> +    }
> +
> +    #[allow(dead_code)]
> +    fn st_weekday(&self) -> bool {
> +        matches!(self, VendorVariant::StV1 | VendorVariant::StV2)
> +    }
> +
> +    #[allow(dead_code)]
> +    fn range_min(&self) -> i64 {
> +        match self {
> +            VendorVariant::Arm | VendorVariant::StV1 => 0,
> +            VendorVariant::StV2 => bindings::RTC_TIMESTAMP_BEGIN_0000,
> +        }
> +    }
> +
> +    #[allow(dead_code)]
> +    fn range_max(&self) -> u64 {
> +        match self {
> +            VendorVariant::Arm | VendorVariant::StV1 => u64::from(u32::MAX),
> +            VendorVariant::StV2 => bindings::RTC_TIMESTAMP_END_9999,
> +        }
> +    }
> +}
> +
> +/// The driver's private data struct. It holds all necessary devres managed
> +/// resources.
> +#[pin_data]
> +struct Pl031DrvData {
> +    #[pin]
> +    regs: Devres<IoMem<PL031_REG_SIZE>>,
> +    hw_variant: VendorVariant,
> +}
> +
> +// SAFETY: `Pl031DrvData` contains only `Send`/`Sync` types: `Devres`
> +// (Send+Sync) and `VendorVariant` (Copy).
> +unsafe impl Send for Pl031DrvData {}
> +// SAFETY: `Pl031DrvData` contains only `Send`/`Sync` types: `Devres`
> +// (Send+Sync) and `VendorVariant` (Copy).
> +unsafe impl Sync for Pl031DrvData {}
> +
> +/// Vendor-specific variant identifier used in AMBA device table.
> +#[derive(Copy, Clone)]
> +struct Pl031Variant {
> +    variant: VendorVariant,
> +}
> +
> +impl Pl031Variant {
> +    const ARM: Self = Self {
> +        variant: VendorVariant::Arm,
> +    };
> +    const STV1: Self = Self {
> +        variant: VendorVariant::StV1,
> +    };
> +    const STV2: Self = Self {
> +        variant: VendorVariant::StV2,
> +    };
> +}
> +
> +// Use AMBA device table for matching
> +kernel::amba_device_table!(
> +    ID_TABLE,
> +    MODULE_ID_TABLE,
> +    <Pl031AmbaDriver as amba::Driver>::IdInfo,
> +    [
> +        (
> +            amba::DeviceId::new(0x00041031, 0x000fffff),
> +            Pl031Variant::ARM
> +        ),
> +        (
> +            amba::DeviceId::new(0x00180031, 0x00ffffff),
> +            Pl031Variant::STV1
> +        ),
> +        (
> +            amba::DeviceId::new(0x00280031, 0x00ffffff),
> +            Pl031Variant::STV2
> +        ),
> +    ]
> +);
> +
> +#[pin_data]
> +struct Pl031AmbaDriver {
> +    #[pin]
> +    irqreg: irq::Registration<Pl031IrqHandler>,
> +}
> +
> +impl amba::Driver for Pl031AmbaDriver {
> +    type IdInfo = Pl031Variant;
> +    const AMBA_ID_TABLE: amba::IdTable<Self::IdInfo> = &ID_TABLE;
> +
> +    fn probe(
> +        adev: &amba::Device<Core>,
> +        id_info: Option<&Self::IdInfo>,
> +    ) -> impl PinInit<Self, Error> {
> +        pin_init::pin_init_scope(move || {
> +            let dev = adev.as_ref();
> +            let io_request = adev.io_request().ok_or(ENODEV)?;
> +            let variant = id_info
> +                .map(|info| info.variant)
> +                .unwrap_or(VendorVariant::Arm);
> +
> +            let rtcdev = RtcDevice::<Pl031DrvData>::new(
> +                dev,
> +                try_pin_init!(Pl031DrvData {
> +                    regs <- IoMem::new(io_request),
> +                    hw_variant: variant,
> +                }),
> +            )?;
> +
> +            dev.devm_init_wakeup()?;
> +
> +            let drvdata = rtcdev.drvdata()?;
> +            let regs = drvdata.regs.access(dev)?;
> +
> +            // Enable the clockwatch on ST Variants
> +            let mut cr = regs.read32(RTC_CR);
> +            if variant.clockwatch() {
> +                cr |= RTC_CR_CWEN;
> +            } else {
> +                cr |= RTC_CR_EN;
> +            }
> +            regs.write32(cr, RTC_CR);
> +
> +            // On ST PL031 variants, the RTC reset value does not provide
> +            // correct weekday for 2000-01-01. Correct the erroneous sunday
> +            // to saturday.
> +            if variant.st_weekday() {
> +                let bcd_year = regs.read32(RTC_YDR);
> +                if bcd_year == 0x2000 {
> +                    let st_time = regs.read32(RTC_DR);
> +                    if (st_time & (RTC_MON_MASK | RTC_MDAY_MASK | RTC_WDAY_MASK)) == 0x02120000 {
> +                        regs.write32(0x2000, RTC_YLR);
> +                        regs.write32(st_time | (0x7 << RTC_WDAY_SHIFT), RTC_LR);
> +                    }
> +                }
> +            }
> +
> +            rtcdev.set_range_min(variant.range_min());
> +            rtcdev.set_range_max(variant.range_max());
> +
> +            // This variant shares the IRQ with another block and must not
> +            // suspend that IRQ line.
> +            let irq_flags = if variant == VendorVariant::StV2 {
> +                kernel::irq::Flags::SHARED | kernel::irq::Flags::COND_SUSPEND
> +            } else {
> +                kernel::irq::Flags::SHARED
> +            };
> +
> +            if adev
> +                .irq_by_index(0)
> +                .and_then(|irq| irq.devm_set_wake_irq())
> +                .is_err()
> +            {
> +                rtcdev.clear_feature(bindings::RTC_FEATURE_ALARM);
> +            }
> +
> +            rtcdev.register()?;
> +
> +            Ok(try_pin_init!(Pl031AmbaDriver {
> +                irqreg <- adev.request_irq_by_index(
> +                    irq_flags,
> +                    0,
> +                    c_str!("rtc-pl031"),
> +                    try_pin_init!(Pl031IrqHandler {
> +                        _pin: PhantomPinned,
> +                        rtcdev: rtcdev.clone(),
> +                    }),
> +                ),
> +            }))
> +        })
> +    }
> +}
> +
> +/// Interrupt handler for PL031 RTC alarm events.
> +#[pin_data]
> +struct Pl031IrqHandler {
> +    #[pin]
> +    _pin: PhantomPinned,
> +    rtcdev: ARef<RtcDevice<Pl031DrvData>>,
> +}
> +
> +impl Handler for Pl031IrqHandler {
> +    fn handle(&self, dev: &device::Device<device::Bound>) -> IrqReturn {
> +        // Get driver data using drvdata.
> +        let drvdata = match self.rtcdev.drvdata() {
> +            Ok(drvdata) => drvdata,
> +            Err(_) => return IrqReturn::None,
> +        };
> +
> +        // Access the MMIO registers.
> +        let regs = match drvdata.regs.access(dev) {
> +            Ok(regs) => regs,
> +            Err(_) => return IrqReturn::None,
> +        };
> +
> +        // Read masked interrupt status.
> +        let rtcmis = regs.read32(RTC_MIS);
> +
> +        if (rtcmis & RTC_BIT_AI) != 0 {
> +            regs.write32(RTC_BIT_AI, RTC_ICR);
> +            self.rtcdev.update_irq(1, (RTC_AF | RTC_IRQF) as usize);
> +            return IrqReturn::Handled;
> +        }
> +
> +        IrqReturn::None
> +    }
> +}
> +
> +#[vtable]
> +impl RtcOps for Pl031DrvData {
> +    fn read_time(
> +        rtcdev: &RtcDevice<Self>,
> +        tm: &mut RtcTime,
> +        parent_dev: &device::Device<device::Bound>,
> +    ) -> Result {
> +        let drvdata = rtcdev.drvdata()?;
> +        let regs = drvdata.regs.access(parent_dev)?;
> +
> +        match drvdata.hw_variant {
> +            VendorVariant::Arm | VendorVariant::StV1 => {
> +                let time32: u32 = regs.read32(RTC_DR);
> +                let time64 = i64::from(time32);
> +                tm.set_from_time64(time64);
> +            }
> +            VendorVariant::StV2 => {
> +                let st_time = regs.read32(RTC_DR);
> +                let bcd_year = regs.read32(RTC_YDR);
> +                *tm = stv2_time_to_tm(st_time, bcd_year);
> +            }
> +        }
> +
> +        Ok(())
> +    }
> +
> +    fn set_time(
> +        rtcdev: &RtcDevice<Self>,
> +        tm: &RtcTime,
> +        parent_dev: &device::Device<device::Bound>,
> +    ) -> Result {
> +        let dev: &device::Device = rtcdev.as_ref();
> +        let drvdata = rtcdev.drvdata()?;
> +        let regs = drvdata.regs.access(parent_dev)?;
> +
> +        match drvdata.hw_variant {
> +            VendorVariant::Arm | VendorVariant::StV1 => {
> +                let time64 = tm.to_time64();
> +                regs.write32(time64 as u32, RTC_LR);
> +            }
> +            VendorVariant::StV2 => {
> +                let (st_time, bcd_year) = stv2_tm_to_time(tm).inspect_err(|&err| {
> +                    if err == EINVAL {
> +                        dev_err!(dev, "invalid wday value {}\n", tm.tm_wday());
> +                    }
> +                })?;
> +                regs.write32(bcd_year, RTC_YLR);
> +                regs.write32(st_time, RTC_LR);
> +            }
> +        }
> +
> +        Ok(())
> +    }
> +
> +    fn read_alarm(
> +        rtcdev: &RtcDevice<Self>,
> +        alarm: &mut RtcWkAlrm,
> +        parent_dev: &device::Device<device::Bound>,
> +    ) -> Result {
> +        let drvdata = rtcdev.drvdata()?;
> +        let regs = drvdata.regs.access(parent_dev)?;
> +
> +        match drvdata.hw_variant {
> +            VendorVariant::Arm | VendorVariant::StV1 => {
> +                let time32: u32 = regs.read32(RTC_MR);
> +                let time64 = i64::from(time32);
> +                RtcTime::time64_to_tm(time64, alarm.get_time_mut());
> +            }
> +            VendorVariant::StV2 => {
> +                let st_time = regs.read32(RTC_MR);
> +                let bcd_year = regs.read32(RTC_YMR);
> +                *alarm.get_time_mut() = stv2_time_to_tm(st_time, bcd_year);
> +            }
> +        }
> +
> +        alarm.set_pending((regs.read32(RTC_RIS) & RTC_BIT_AI) != 0);
> +        alarm.set_enabled((regs.read32(RTC_IMSC) & RTC_BIT_AI) != 0);
> +
> +        Ok(())
> +    }
> +
> +    fn set_alarm(
> +        rtcdev: &RtcDevice<Self>,
> +        alarm: &RtcWkAlrm,
> +        parent_dev: &device::Device<device::Bound>,
> +    ) -> Result {
> +        let dev: &device::Device = rtcdev.as_ref();
> +        let drvdata = rtcdev.drvdata()?;
> +        let regs = drvdata.regs.access(parent_dev)?;
> +
> +        match drvdata.hw_variant {
> +            VendorVariant::Arm | VendorVariant::StV1 => {
> +                let time64 = alarm.get_time().to_time64();
> +                regs.write32(time64 as u32, RTC_MR);
> +            }
> +            VendorVariant::StV2 => {
> +                let (st_time, bcd_year) =
> +                    stv2_tm_to_time(alarm.get_time()).inspect_err(|&err| {
> +                        if err == EINVAL {
> +                            dev_err!(dev, "invalid wday value {}\n", alarm.get_time().tm_wday());
> +                        }
> +                    })?;
> +                regs.write32(bcd_year, RTC_YMR);
> +                regs.write32(st_time, RTC_MR);
> +            }
> +        }
> +
> +        Self::alarm_irq_enable(rtcdev, alarm.enabled(), parent_dev)
> +    }
> +
> +    fn alarm_irq_enable(
> +        rtcdev: &RtcDevice<Self>,
> +        enabled: bool,
> +        parent_dev: &device::Device<device::Bound>,
> +    ) -> Result {
> +        let drvdata = rtcdev.drvdata()?;
> +        let regs = drvdata.regs.access(parent_dev)?;
> +
> +        // Clear any pending alarm interrupts.
> +        regs.write32(RTC_BIT_AI, RTC_ICR);
> +
> +        let mut imsc = regs.read32(RTC_IMSC);
> +        if enabled {
> +            imsc |= RTC_BIT_AI;
> +        } else {
> +            imsc &= !RTC_BIT_AI;
> +        }
> +        regs.write32(imsc, RTC_IMSC);
> +
> +        Ok(())
> +    }
> +}
> +
> +kernel::module_amba_driver! {
> +    type: Pl031AmbaDriver,
> +    name: "rtc-pl031-rust",
> +    authors: ["Ke Sun <sunke@kylinos.cn>"],
> +    description: "Rust PL031 RTC driver",
> +    license: "GPL v2",
> +    imports_ns: ["RTC"],
> +}

      reply	other threads:[~2026-01-19  9:12 UTC|newest]

Thread overview: 43+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-01-16 16:21 [RFC PATCH v3 0/5] rust: Add RTC driver support Ke Sun
2026-01-16 16:21 ` [RFC PATCH v3 1/5] rtc: add device selector for rtc_class_ops callbacks Ke Sun
2026-01-16 16:24   ` Ke Sun
2026-01-19 14:32   ` Danilo Krummrich
2026-01-20  8:01     ` Ke Sun
2026-02-20 22:53       ` Alexandre Belloni
2026-02-21  9:31         ` Alvin Sun
2026-02-21 11:16           ` Alexandre Belloni
2026-02-21 11:19             ` Rafael J. Wysocki
2026-02-21 14:33               ` Danilo Krummrich
2026-02-22  0:05                 ` Alexandre Belloni
2026-02-22 12:49                   ` Danilo Krummrich
2026-02-22 14:01                     ` Rafael J. Wysocki
2026-02-22 16:13                       ` Danilo Krummrich
2026-02-24  0:12                         ` Danilo Krummrich
2026-02-24 13:28                           ` Rafael J. Wysocki
2026-02-24 14:57                             ` Alexandre Belloni
2026-02-24 15:23                               ` Rafael J. Wysocki
2026-02-24 15:36                                 ` Danilo Krummrich
2026-02-24 15:01                           ` Alexandre Belloni
2026-02-24 16:35                             ` Danilo Krummrich
2026-02-24 16:42                               ` Danilo Krummrich
2026-02-24 17:28                               ` Alexandre Belloni
2026-02-24 22:23                                 ` Danilo Krummrich
2026-02-24 22:44                                   ` Alexandre Belloni
2026-02-25  3:19                                     ` Gary Guo
2026-02-25 13:33                                   ` Rafael J. Wysocki
2026-02-25 16:26                                     ` Danilo Krummrich
2026-02-25 21:15                                       ` Rafael J. Wysocki
2026-02-26 12:28                                       ` Rafael J. Wysocki
2026-02-27 15:09                                       ` Benno Lossin
2026-02-22 12:25                 ` Rafael J. Wysocki
2026-02-22 14:24                   ` Rafael J. Wysocki
2026-02-22 15:29                   ` Danilo Krummrich
2026-02-22 15:43                     ` Rafael J. Wysocki
2026-02-21 16:32             ` Alvin Sun
2026-02-21 17:53             ` Danilo Krummrich
2026-01-16 16:22 ` [RFC PATCH v3 2/5] rust: add AMBA bus driver support Ke Sun
2026-01-16 16:22 ` [RFC PATCH v3 3/5] rust: add device wakeup capability support Ke Sun
2026-01-17  0:44   ` Ke Sun
2026-01-16 16:22 ` [RFC PATCH v3 4/5] rust: add RTC core abstractions and data structures Ke Sun
2026-01-16 16:34 ` [RFC PATCH v3 5/5] rust: add PL031 RTC driver Ke Sun
2026-01-19  9:12   ` Ke Sun [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=54d8007e-c54e-44a2-8cfe-df10495cda80@gmail.com \
    --to=sk.alvin.x@gmail.com \
    --cc=a.hindborg@kernel.org \
    --cc=alexandre.belloni@bootlin.com \
    --cc=aliceryhl@google.com \
    --cc=bjorn3_gh@protonmail.com \
    --cc=boqun.feng@gmail.com \
    --cc=dakr@kernel.org \
    --cc=gary@garyguo.net \
    --cc=linux-rtc@vger.kernel.org \
    --cc=lossin@kernel.org \
    --cc=ojeda@kernel.org \
    --cc=rust-for-linux@vger.kernel.org \
    --cc=sunke@kylinos.cn \
    --cc=tmgross@umich.edu \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox