From 5aaf7cc45c7d4dd3da4d2c9a3874af320443e96d Mon Sep 17 00:00:00 2001 From: Mateusz Sulimowicz Date: Tue, 29 Jul 2025 11:47:13 +0000 Subject: [PATCH] Implement redistributor power on/off GIC-600 and GIC700 must be manually powered on. --- src/gicv3.rs | 79 ++++++++++++++++++++++++++++++++++++++++-- src/gicv3/registers.rs | 70 ++++++++++++++++++++++++++++++++++--- 2 files changed, 142 insertions(+), 7 deletions(-) diff --git a/src/gicv3.rs b/src/gicv3.rs index 7fd45ea..1aa0d57 100644 --- a/src/gicv3.rs +++ b/src/gicv3.rs @@ -16,9 +16,9 @@ use crate::sysreg::{ }; use crate::{IntId, Trigger}; use core::{hint::spin_loop, ptr::NonNull}; -use registers::{GicrSgi, GicrTyper, Typer}; +use registers::{GicrIidr, GicrPwrr, GicrSgi, GicrTyper, Typer}; use safe_mmio::fields::ReadPureWrite; -use safe_mmio::{UniqueMmioPointer, field, field_shared, split_fields}; +use safe_mmio::{SharedMmioPointer, UniqueMmioPointer, field, field_shared, split_fields}; use thiserror::Error; /// An error which may be returned from operations on a GIC Redistributor. @@ -482,6 +482,81 @@ impl GicV3<'_> { while field_shared!(gicr, ctlr).read().contains(GicrCtlr::RWP) {} } + fn gicr_wait_until_group_not_in_transit(gicr_ptr: &SharedMmioPointer) { + let pwrr = field_shared!(gicr_ptr, pwrr).read(); + + // Check group not transitioning + while pwrr.contains(GicrPwrr::RedistributorGroupPowerDown) + != pwrr.contains(GicrPwrr::RedistributorGroupPoweredOff) + { + spin_loop(); + } + } + + fn gicr_needs_power_management(gicr_ptr: &SharedMmioPointer) -> bool { + let iidr: GicrIidr = field_shared!(gicr_ptr, iidr).read(); + + iidr.model_id() == GicrIidr::MODEL_ID_ARM_GIC_600 + || iidr.model_id() == GicrIidr::MODEL_ID_ARM_GIC_600AE + || iidr.model_id() == GicrIidr::MODEL_ID_ARM_GIC_700 + } + + fn gic600_gic700_gicr_power_on(mut gicr_ptr: UniqueMmioPointer) { + loop { + // Wait until group not transitioning. + Self::gicr_wait_until_group_not_in_transit(&gicr_ptr); + + // Power on the redistributor. + field!(gicr_ptr, pwrr).write(GicrPwrr::empty()); + + // Wait until the power on state is reflected. + // If RDPD == 0 then powered on. + if !field_shared!(gicr_ptr, pwrr) + .read() + .contains(GicrPwrr::RedistributorPowerDown) + { + break; + } + } + } + + fn gic600_gic700_gicr_power_off(mut gicr_ptr: UniqueMmioPointer) { + // Wait until group not transitioning. + Self::gicr_wait_until_group_not_in_transit(&gicr_ptr); + + // Power off the redistributor. + field!(gicr_ptr, pwrr).write(GicrPwrr::RedistributorPowerDown); + + // If this is the last man, turning this redistributor frame off will + // result in the group itself being powered off and RDGPD = 1. + // In that case, wait as long as it's in transition, or has aborted + // the transition altogether for any reason. + if field_shared!(gicr_ptr, pwrr) + .read() + .contains(GicrPwrr::RedistributorGroupPowerDown) + { + Self::gicr_wait_until_group_not_in_transit(&gicr_ptr); + } + } + + /// Power on GIC-600 or GIC-700 redistributor (if detected). + pub fn gicr_power_on(&mut self, cpu: usize) { + let gicr_ptr = self.gicr_ptr(cpu); + + if Self::gicr_needs_power_management(&gicr_ptr) { + Self::gic600_gic700_gicr_power_on(gicr_ptr); + } + } + + /// Power off GIC-600 or GIC-700 redistributor (if detected). + pub fn gicr_power_off(&mut self, cpu: usize) { + let gicr_ptr = self.gicr_ptr(cpu); + + if Self::gicr_needs_power_management(&gicr_ptr) { + Self::gic600_gic700_gicr_power_off(gicr_ptr); + } + } + /// Informs the GIC redistributor that the core has awakened. /// /// Blocks until `GICR_WAKER.ChildrenAsleep` is cleared. diff --git a/src/gicv3/registers.rs b/src/gicv3/registers.rs index 2453651..4ea1fe9 100644 --- a/src/gicv3/registers.rs +++ b/src/gicv3/registers.rs @@ -65,6 +65,62 @@ impl Debug for GicrCtlr { } } +/// This register controls the powerup sequence of the Redistributors. +/// +/// It is implemented only for GIC-600 and GIC-700 +/// and is used to power on/off the redistributors. +#[repr(transparent)] +#[derive(Copy, Clone, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)] +pub struct GicrPwrr(u32); + +bitflags! { + impl GicrPwrr: u32 { + /// (RDGPO) + /// This bit indicates: + /// 0 = GCI (GIC Cluster Interface) is powered up and can be accessed. + /// 1 = It is safe to power down the GCI. + const RedistributorGroupPoweredOff = 1 << 3; + + /// (RDGPD) + /// This bit indicates the intentional power state of the GCI: + /// 0 = Intend to power up + /// 1 = Intend to power down + /// The GCI has reached its intentional power state when RDGPD = RDGPO. + const RedistributorGroupPowerDown = 1 << 2; + + /// (RDAG) + /// Setting this bit to 1 applies the RDPD value to all Redistributors + /// on the same GCI. + /// If the RDPD value cannot be applied to all cores in the group, + /// then the GIC ignores this request. + const RedistributorApplyGroup = 1 << 1; + + /// (RDPD) + /// 0 = Redistributor is powered up and can be accessed. + /// 1 = The core permits the Redistributor to be powered down. + const RedistributorPowerDown = 1 << 0; + } +} + +/// This register provides information about +/// the implementer and revision of the Redistributor. +#[repr(transparent)] +#[derive(Copy, Clone, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)] +pub struct GicrIidr(u32); + +impl GicrIidr { + pub const MODEL_ID_ARM_GIC_600: u32 = 0x0200043b; + pub const MODEL_ID_ARM_GIC_600AE: u32 = 0x0300043b; + pub const MODEL_ID_ARM_GIC_700: u32 = 0x0400043b; + + /// Returns model ID of the redistributor. + pub fn model_id(self) -> u32 { + const PRODUCT_ID_MASK: u32 = 0xff << 24; + const IMPLEMENTER_MASK: u32 = 0xfff; + self.0 & (PRODUCT_ID_MASK | IMPLEMENTER_MASK) + } +} + /// Interrupt controller redistributor type register value. #[derive(Clone, Copy, Debug, Eq, FromBytes, Immutable, IntoBytes, KnownLayout, PartialEq)] #[repr(transparent)] @@ -394,7 +450,7 @@ pub struct Gicr { /// Redistributor control register. pub ctlr: ReadPureWrite, /// Implementer identification register. - pub iidr: u32, + pub iidr: ReadPure, /// Redistributor type register. pub typer: ReadPure, /// Error reporting status register. @@ -406,7 +462,11 @@ pub struct Gicr { /// Set PARTID and PMG register. pub partidr: u32, /// Implementation defined registers. - pub implementation_defined1: [u32; 8], + pub implementation_defined1: u32, + /// Redistributor power register (implemented in GIC-600 and GIC-700). + pub pwrr: ReadPureWrite, + /// Implementation defined registers. + pub implementation_defined2: [u32; 6], /// Set LPI pending register. pub setlprir: u64, /// Clear LPI pending register. @@ -427,13 +487,13 @@ pub struct Gicr { pub syncr: u32, _reserved4: [u32; 15], /// Implementation defined registers. - pub implementation_defined2: u64, + pub implementation_defined3: u64, _reserved5: u64, /// Implementation defined registers. - pub implementation_defined3: u64, + pub implementation_defined4: u64, _reserved6: [u32; 12218], /// Implementation defined registers. - pub implementation_defined4: [u32; 4084], + pub implementation_defined5: [u32; 4084], /// ID registers. pub id_registers: [u32; 12], }