start adding GIC and GTP driver

This commit is contained in:
Robin Müller 2025-03-04 07:55:48 +01:00
parent 7279c96f03
commit ae6e78e93b
Signed by: muellerr
GPG Key ID: A649FB78196E3849
10 changed files with 459 additions and 26 deletions

View File

@ -3,6 +3,7 @@
use core::panic::PanicInfo;
use cortex_ar::asm::nop;
use zynq7000::{hal::gic::Gic, pac::gic::{Gicc, Gicd}};
use zynq7000_rt as _;
/// One user LED is MIO7
@ -19,6 +20,11 @@ pub extern "C" fn boot_core(cpu_id: u32) -> ! {
#[unsafe(export_name = "main")]
pub fn main() -> ! {
let mut gic = Gic::new(unsafe { Gicc::new_mmio() }, unsafe { Gicd::new_mmio() });
gic.enable_all_interrupts();
gic.set_all_spi_interrupt_targets_cpu0();
gic.enable();
let mut gpio = unsafe { zynq7000::pac::gpio::Gpio::new_mmio() };
gpio.modify_dirm_0(|v| v | ZEDBOARD_LED_MASK);
gpio.modify_out_en_0(|v| v | ZEDBOARD_LED_MASK);
@ -30,6 +36,12 @@ pub fn main() -> ! {
}
}
#[unsafe(no_mangle)]
pub extern "C" fn _irq_handler() {
let mut gic = Gic::new(unsafe { Gicc::new_mmio() }, unsafe { Gicd::new_mmio() });
}
/// Panic handler
#[panic_handler]
fn panic(_info: &PanicInfo) -> ! {

View File

@ -16,6 +16,10 @@ derive-mmio = { path = "../../derive-mmio", default-features = false }
bitbybit = "1.3"
arbitrary-int = "1.3"
thiserror = { version = "2", default-features = false }
num_enum = { version = "0.7", default-features = false }
critical-section = "1"
embassy-time-driver = "0.2"
embassy-time-queue-utils = "0.1"
# cortex-r
# defmt = { version = "0.3", optional = true }
# critical-section = { version = "1", optional = true }

View File

@ -1,14 +1,4 @@
use crate::pac::gic::{MmioGicc, MmioGicd};
pub struct Gic {
pub gicc: MmioGicc<'static>,
pub gicd: MmioGicd<'static>,
}
pub enum SpiSensitivity {
Level = 0b01,
Edge = 0b11,
}
use crate::pac::gic::{Dcr, Icr, MmioGicc, MmioGicd};
pub const ICFR_2_FIXED_VALUE: u32 = 0b01010101010111010101010001011111;
/// This configures PL[2:0] to high-level sensitivity.
@ -18,13 +8,159 @@ pub const ICFR_4_FIXED_VALUE: u32 = 0b01110101010101010101010101010101;
/// This configures PL[15:8] to high-level sensitivity.
pub const ICFR_5_FIXED_VALUE: u32 = 0b00000011010101010101010101010101;
pub const TARGETS_ALL_CPU_0_IPTR_VAL: u32 = 0x01010101;
pub const TARGETS_ALL_CPU_1_IPTR_VAL: u32 = 0x02020202;
pub const ACTIVATE_ALL_SGIS_MASK_ISER: u32 = 0x0000_FFFF;
pub const ACTIVATE_ALL_PPIS_MASK_ISER: u32 = 0xF800_0000;
pub enum SpiSensitivity {
Level = 0b01,
Edge = 0b11,
}
pub enum TargetCpu {
None = 0b00,
Cpu0 = 0b01,
Cpu1 = 0b10,
Both = 0b11,
}
/// Private Peripheral Interrupt (PPI) which are private to the CPU.
#[derive(Debug, Eq, PartialEq, Clone, Copy, num_enum::TryFromPrimitive)]
#[repr(u8)]
pub enum PpiInterrupt {
GlobalTimer = 27,
// Interrupt signal from the PL. CPU0: IRQF2P[18] and CPU1: IRQF2P[19]
NFiq = 28,
CpuPrivateTimer = 29,
/// AWDT0 and AWDT1 for each CPU.
Awdt = 30,
// Interrupt signal from the PL. CPU0: IRQF2P[16] and CPU1: IRQF2P[17]
NIrq = 31,
}
#[derive(Debug, Eq, PartialEq, Clone, Copy, num_enum::TryFromPrimitive)]
#[repr(u8)]
pub enum SpiInterrupt {
Cpu0 = 32,
Cpu1 = 33,
L2Cache = 34,
Ocm = 35,
_Reserved0 = 36,
Pmu0 = 37,
Pmu1 = 38,
Xadc = 39,
DevC = 40,
Swdt = 41,
Ttc00 = 42,
Ttc01 = 43,
Ttc02 = 44,
DmacAbort = 45,
Dmac0 = 46,
Dmac1 = 47,
Dmac2 = 48,
Dmac3 = 49,
Smc = 50,
Qspi = 51,
Gpio = 52,
Usb0 = 53,
Eth0 = 54,
Eth0Wakeup = 55,
Sdio0 = 56,
I2c0 = 57,
Spi0 = 58,
Uart0 = 59,
Can0 = 60,
Pl0 = 61,
Pl1 = 62,
Pl2 = 63,
Pl3 = 64,
Pl4 = 65,
Pl5 = 66,
Pl6 = 67,
Pl7 = 68,
Ttc10 = 69,
Ttc11 = 70,
Ttc12 = 71,
Dmac4 = 72,
Dmac5 = 73,
Dmac6 = 74,
Dmac7 = 75,
Usb1 = 76,
Eth1 = 77,
Eth1Wakeup = 78,
Sdio1 = 79,
I2c1 = 80,
Spi1 = 81,
Uart1 = 82,
Can1 = 83,
Pl8 = 84,
Pl9 = 85,
Pl10 = 86,
Pl11 = 87,
Pl12 = 88,
Pl13 = 89,
Pl14 = 90,
Pl15 = 91,
ScuParity = 92,
}
#[derive(Debug, thiserror::Error)]
#[error("Invalid PL interrupt ID {0}")]
pub struct InvalidPlInterruptId(pub u32);
pub struct InvalidPlInterruptId(pub usize);
/// Invalid Shared Peripheral Interrupt (SPI) ID.
#[derive(Debug, thiserror::Error)]
#[error("Invalid SPI interrupt ID {0}")]
pub struct InvalidSpiInterruptId(pub usize);
/// Invalid Software Generated Interrupt (SGI) ID.
#[derive(Debug, thiserror::Error)]
#[error("Invalid SGI interrupt ID {0}")]
pub struct InvalidSgiInterruptId(pub usize);
/// Higher-level GIC controller for the Zynq70000 SoC.
///
/// The flow of using this controller is as follows:
///
/// 1. Create the controller using [Self::new]. You can use the [crate::pac::gic::Gicc::new_mmio]
/// and [crate::pac::gic::Gicd::new_mmio] functions to create the MMIO instances. The
/// constructor configures all PL interrupts sensivities to high-level sensitivity and
/// configures all sensitivities which are expected to have a certain value.
/// 2. Perform the configuration of the interrupt targets and the interrupt sensitivities.
/// The CPU targets are encoded with [TargetCpu] while the sensitivities are encoded by
/// the [SpiSensitivity] enum. You can use the following API to configure the interrupts:
///
/// - [Self::set_spi_interrupt_cpu_target]
/// - [Self::set_all_spi_interrupt_targets_cpu0]
/// - [Self::set_pl_interrupt_sensitivity]
///
/// 3. Enable all required interrupts. The following API can be used for this:
///
/// - [Self::enable_sgi_interrupt]
/// - [Self::enable_ppi_interrupt]
/// - [Self::enable_spi_interrupt]
/// - [Self::enable_all_spi_interrupts]
/// - [Self::enable_all_ppi_interrupts]
/// - [Self::enable_all_sgi_interrupts]
/// - [Self::enable_all_interrupts]
///
/// You might also chose to enable these interrupts at run-time after the GIC was started.
/// 4. Start the GIC by calling [Self::update_ctrl_regs] with the required settings or
/// with [Self::enable] which assumes a certain configuration.
pub struct Gic {
pub gicc: MmioGicc<'static>,
pub gicd: MmioGicd<'static>,
}
impl Gic {
/// Create a new GIC controller instance and calls [Self::initialize] to perform
/// strongly recommended initialization routines for the GIC.
///
#[inline]
pub fn new(gicc: MmioGicc<'static>, gicd: MmioGicd<'static>) -> Self {
Gic { gicc, gicd }
let mut gic = Gic { gicc, gicd };
gic.initialize();
gic
}
/// Sets up the GIC by configuring the required sensitivites for the shared peripheral
@ -35,6 +171,7 @@ impl Gic {
/// logic. These are configured to high level sensitivity by this function.
/// If you need a different sensitivity, you need to update the bits using the
/// [Self::set_pl_interrupt_sensitivity] function.
#[inline]
pub fn initialize(&mut self) {
self.gicd.write_icfr_2_spi(ICFR_2_FIXED_VALUE);
self.gicd.write_icfr_3_spi(ICFR_3_FIXED_VALUE);
@ -42,9 +179,15 @@ impl Gic {
self.gicd.write_icfr_5_spi(ICFR_5_FIXED_VALUE);
}
/// Set the sensitivity of a the Programmable Logic SPI interrupts.
///
/// These are the only interrupt IDs which are configurable for SPI. They are set
/// to high-level sensitivity by default by the [Self::initialize] function. You can
/// use this method to override certain sensitivies.
#[inline]
pub fn set_pl_interrupt_sensitivity(
&mut self,
pl_int_id: u32,
pl_int_id: usize,
sensitivity: SpiSensitivity,
) -> Result<(), InvalidPlInterruptId> {
if pl_int_id >= 16 {
@ -73,4 +216,144 @@ impl Gic {
}
Ok(())
}
/// Set the CPU target for a SPI interrupt.
///
/// See [Self::set_all_spi_interrupt_targets_cpu0] for a utility method to handle all
/// interrupts with one core.
#[inline]
pub fn set_spi_interrupt_cpu_target(&mut self, spi_int: SpiInterrupt, target: TargetCpu) {
let spi_int_raw = spi_int as u32;
let spi_offset_to_0 = spi_int_raw as usize - 32;
// Unwrap okay, calculated index is always valid.
self.gicd
.write_iptr_spi(
spi_offset_to_0 / 4,
(target as u32) << ((spi_offset_to_0 % 4) * 8),
)
.unwrap();
}
/// Utility function to set all SGI interrupt targets to CPU0.
///
/// This is useful if only CPU0 is active in a system, or if CPU0 handles most interrupts in
/// the system.
#[inline]
pub fn set_all_spi_interrupt_targets_cpu0(&mut self) {
self.gicd
.write_iptr_spi(0, TARGETS_ALL_CPU_0_IPTR_VAL)
.unwrap();
self.gicd
.write_iptr_spi(1, TARGETS_ALL_CPU_0_IPTR_VAL)
.unwrap();
}
#[inline]
pub fn enable_sgi_interrupt(&mut self, int_id: usize) -> Result<(), InvalidSpiInterruptId> {
if int_id >= 16 {
return Err(InvalidSpiInterruptId(int_id));
}
unsafe { self.gicd.write_iser_unchecked(0, 1 << int_id) };
Ok(())
}
#[inline]
pub fn enable_all_sgi_interrupts(&mut self) {
// Unwrap okay, index is valid.
self.gicd
.modify_iser(0, |mut v| {
v |= ACTIVATE_ALL_SGIS_MASK_ISER;
v
})
.unwrap();
}
#[inline]
pub fn enable_ppi_interrupt(&mut self, ppi_int: PpiInterrupt) {
// Unwrap okay, index is valid.
self.gicd
.modify_iser(0, |mut v| {
v |= 1 << (ppi_int as u32);
v
})
.unwrap();
}
#[inline]
pub fn enable_all_ppi_interrupts(&mut self) {
unsafe {
self.gicd.modify_iser_unchecked(0, |mut v| {
v |= ACTIVATE_ALL_PPIS_MASK_ISER;
v
})
};
}
#[inline]
pub fn enable_spi_interrupt(&mut self, spi_int: SpiInterrupt) {
let spi_int_raw = spi_int as u32;
match spi_int_raw {
32..=63 => {
let bit_pos = spi_int_raw - 32;
// Unwrap okay, valid index.
self.gicd.write_iser(1, 1 << bit_pos).unwrap();
}
64..=92 => {
let bit_pos = spi_int_raw - 64;
// Unwrap okay, valid index.
self.gicd.write_iser(2, 1 << bit_pos).unwrap();
}
_ => unreachable!(),
}
}
#[inline]
pub fn enable_all_spi_interrupts(&mut self) {
self.gicd.write_iser(1, 0xFFFF_FFFF).unwrap();
self.gicd.write_iser(2, 0xFFFF_FFFF).unwrap();
}
/// Enables all interrupts by calling [Self::enable_all_sgi_interrupts],
/// [Self::enable_all_ppi_interrupts] and [Self::enable_all_spi_interrupts].
pub fn enable_all_interrupts(&mut self) {
self.enable_all_sgi_interrupts();
self.enable_all_ppi_interrupts();
self.enable_all_spi_interrupts();
}
/// Enable the GIC assuming a possibly non-secure configuration.
///
/// This function will NOT configure and enable the various interrupt sources. You need to
/// set the interrupt
/// This function configured the control registers with the following settings:
///
/// - CPU interface: Secure and non-secure interrupts are enabled. SBPR, FIQen and AckCtrl
/// fields set to default value 0.
/// - Distributor interface: Both non-secure and secure interrupt distribution enabled.
///
/// It calls [Self::update_ctrl_regs] to update the control registers.
/// If you need custom settings, you can call [Self::update_ctrl_regs] with your required
/// settings.
pub fn enable(&mut self) {
self.update_ctrl_regs(
Icr::builder()
.with_sbpr(false)
.with_fiq_en(false)
.with_ack_ctrl(false)
.with_enable_non_secure(true)
.with_enable_secure(true)
.build(),
Dcr::builder()
.with_enable_non_secure(true)
.with_enable_secure(true)
.build(),
);
}
/// Update the control registers which control the safety configuration and which also enable
/// the GIC.
pub fn update_ctrl_regs(&mut self, icr: Icr, dcr: Dcr) {
self.gicc.write_icr(icr);
self.gicd.write_dcr(dcr);
}
}

34
zynq7000/src/hal/gtc.rs Normal file
View File

@ -0,0 +1,34 @@
use core::{
cell::RefCell,
sync::atomic::{compiler_fence, Ordering},
};
use crate::pac::gtc::MmioGtc;
/// High level GTC driver.
pub struct Gtc(pub RefCell<MmioGtc<'static>>);
impl Gtc {
#[inline]
pub fn new(mmio_gtc: MmioGtc<'static>) -> Self {
Self(RefCell::new(mmio_gtc))
}
#[inline]
pub unsafe fn steal() -> Self {
Self::new(unsafe { crate::pac::gtc::Gtc::new_mmio() })
}
#[inline]
pub fn read_timer(&self) -> u64 {
let mut periph = self.0.borrow_mut();
let upper = periph.read_count_upper();
loop {
let lower = periph.read_count_lower();
if periph.read_count_upper() == upper {
return ((upper as u64) << 32) | (lower as u64);
}
// Overflow, read upper again.
}
}
}

View File

@ -1 +1,2 @@
pub mod gic;
pub mod gtc;

View File

@ -3,5 +3,6 @@
pub const MPCORE_BASE_ADDR: usize = 0xF8F0_0000;
pub mod pac;
pub mod hal;
pub mod pac;
pub mod time_driver;

View File

@ -1,9 +1,9 @@
pub use crate::pac::mpcore::{GICC_BASE_ADDR, GICD_BASE_ADDR};
use arbitrary_int::{u10, u3, u5};
use arbitrary_int::{u3, u5, u10};
use static_assertions::const_assert_eq;
/// Distributor Control Register
#[bitbybit::bitfield(u32)]
#[bitbybit::bitfield(u32, default = 0x0)]
pub struct Dcr {
#[bit(1, rw)]
enable_non_secure: bool,
@ -75,7 +75,10 @@ pub struct Gicd {
pub ipr: [u32; 0x18],
_reserved_11: [u32; 0xE8],
/// Interrupt Processor Targes Registers
pub iptr: [u32; 0x18],
pub iptr_sgi: [u32; 0x4],
// TODO: Mark those read-only as soon as that works for arrays.
pub iptr_ppi: [u32; 0x4],
pub iptr_spi: [u32; 0x10],
// Those are split in the ARM documentation for some reason..
_reserved_12: [u32; 0xE8],
/// Interrupt Configuration Registers
@ -125,7 +128,7 @@ impl Gicd {
}
/// CPU interface control register.
#[bitbybit::bitfield(u32)]
#[bitbybit::bitfield(u32, default = 0x0)]
pub struct Icr {
#[bit(4, rw)]
sbpr: bool,

View File

@ -2,6 +2,24 @@
pub const GTC_BASE_ADDR: usize = crate::pac::mpcore::MPCORE_BASE_ADDR + 0x0000_0200;
#[bitbybit::bitfield(u32)]
pub struct GtcCtrl {
#[bits(8..=15, rw)]
prescaler: u8,
#[bit(2, rw)]
irq_enable: bool,
#[bit(1, rw)]
comparator_enable: bool,
#[bit(0, rw)]
enable: bool,
}
#[bitbybit::bitfield(u32)]
pub struct InterruptStatus {
#[bit(0, rw)]
event_flag: bool,
}
/// Global timer counter.
#[derive(derive_mmio::Mmio)]
#[mmio(no_ctors)]
@ -12,9 +30,9 @@ pub struct Gtc {
/// Count register 1, upper 32 bits
count_upper: u32,
/// Control register
ctrl: u32,
ctrl: GtcCtrl,
/// Interrupt status register
isr: u32,
isr: InterruptStatus,
/// Comparator 0, lower 32 bits
comparator_lower: u32,
/// Comparator 1, upper 32 bits

View File

@ -1,6 +1,6 @@
pub mod gpio;
pub mod uart;
pub mod gtc;
pub mod slcr;
pub mod mpcore;
pub mod gic;
pub mod gpio;
pub mod gtc;
pub mod mpcore;
pub mod slcr;
pub mod uart;

View File

@ -0,0 +1,77 @@
use core::{
cell::{Cell, RefCell},
sync::atomic::AtomicU64,
};
use critical_section::{CriticalSection, Mutex};
use embassy_time_driver::Driver;
use embassy_time_queue_utils::Queue;
struct AlarmState {
timestamp: Cell<u64>,
}
impl AlarmState {
const fn new() -> Self {
Self {
timestamp: Cell::new(u64::MAX),
}
}
}
unsafe impl Send for AlarmState {}
pub struct TimerDriver {
periods: AtomicU64,
/// Timestamp at which to fire alarm. u64::MAX if no alarm is scheduled.
alarms: Mutex<AlarmState>,
queue: Mutex<RefCell<Queue>>,
}
impl TimerDriver {
fn set_alarm(&self, cs: CriticalSection, timestamp: u64) -> bool {
true
}
}
impl Driver for TimerDriver {
fn now(&self) -> u64 {
/*
if SCALE.get().is_none() {
return 0;
}
let mut period1: u32;
let mut period2: u32;
let mut counter_val: u32;
loop {
// Acquire ensures that we get the latest value of `periods` and
// no instructions can be reordered before the load.
period1 = self.periods.load(Ordering::Acquire);
counter_val = u32::MAX - Self::timekeeper_tim().cnt_value().read().bits();
// Double read to protect against race conditions when the counter is overflowing.
period2 = self.periods.load(Ordering::Relaxed);
if period1 == period2 {
let now = (((period1 as u64) << 32) | counter_val as u64) / *SCALE.get().unwrap();
return now;
}
}
*/
}
fn schedule_wake(&self, at: u64, waker: &core::task::Waker) {
critical_section::with(|cs| {
let mut queue = self.queue.borrow(cs).borrow_mut();
if queue.schedule_wake(at, waker) {
let mut next = queue.next_expiration(self.now());
while !self.set_alarm(cs, next) {
next = queue.next_expiration(self.now());
}
}
})
}
}