Skip to content
This repository was archived by the owner on Oct 18, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 77 additions & 2 deletions src/gicv3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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<Gicr>) {
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<Gicr>) -> 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<Gicr>) {
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<Gicr>) {
// 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.
Expand Down
70 changes: 65 additions & 5 deletions src/gicv3/registers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Comment thread
mateusz-sulimowicz marked this conversation as resolved.

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);
Comment thread
mateusz-sulimowicz marked this conversation as resolved.

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)]
Expand Down Expand Up @@ -394,7 +450,7 @@ pub struct Gicr {
/// Redistributor control register.
pub ctlr: ReadPureWrite<GicrCtlr>,
/// Implementer identification register.
pub iidr: u32,
pub iidr: ReadPure<GicrIidr>,
/// Redistributor type register.
pub typer: ReadPure<GicrTyper>,
/// Error reporting status register.
Expand All @@ -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<GicrPwrr>,
/// Implementation defined registers.
pub implementation_defined2: [u32; 6],
/// Set LPI pending register.
pub setlprir: u64,
/// Clear LPI pending register.
Expand All @@ -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],
}
Expand Down
Loading