diff --git a/lib/device-api/src/lib.rs b/lib/device-api/src/lib.rs index d73fceb6..32427ea0 100644 --- a/lib/device-api/src/lib.rs +++ b/lib/device-api/src/lib.rs @@ -22,3 +22,7 @@ pub trait CpuBringupDevice: Device { /// misused. unsafe fn start_cpu(&self, id: usize, ip: usize, arg0: usize) -> Result<(), Error>; } + +pub trait ResetDevice: Device { + unsafe fn reset(&self) -> !; +} diff --git a/src/arch/aarch64/mod.rs b/src/arch/aarch64/mod.rs index a89fe599..506ca1aa 100644 --- a/src/arch/aarch64/mod.rs +++ b/src/arch/aarch64/mod.rs @@ -15,6 +15,7 @@ use device_api::{ ExternalInterruptController, IpiDeliveryTarget, IrqNumber, LocalInterruptController, }, timer::MonotonicTimestampProviderDevice, + ResetDevice, }; use fdt_rs::prelude::PropReader; use git_version::git_version; @@ -70,6 +71,7 @@ pub struct AArch64 { ext_intc: OneTimeInit<&'static dyn ExternalInterruptController>, local_intc: OneTimeInit<&'static dyn LocalInterruptController>, mtimer: OneTimeInit<&'static dyn MonotonicTimestampProviderDevice>, + reset: OneTimeInit<&'static dyn ResetDevice>, // ARM-only devices /// ARM PSCI instance on this system (there may not be one) @@ -82,6 +84,7 @@ pub static ARCHITECTURE: AArch64 = AArch64 { ext_intc: OneTimeInit::new(), local_intc: OneTimeInit::new(), mtimer: OneTimeInit::new(), + reset: OneTimeInit::new(), // ARM-only devices psci: OneTimeInit::new(), @@ -180,6 +183,11 @@ impl Architecture for AArch64 { Ok(()) } + fn register_reset_device(&self, reset: &'static dyn ResetDevice) -> Result<(), Error> { + self.reset.init(reset); + Ok(()) + } + fn external_interrupt_controller(&self) -> &'static dyn ExternalInterruptController { *self.ext_intc.get() } @@ -203,7 +211,8 @@ impl Architecture for AArch64 { } unsafe fn reset(&self) -> ! { - todo!() + let reset = self.reset.get(); + reset.reset(); } } diff --git a/src/arch/mod.rs b/src/arch/mod.rs index a3b0f803..b30e5437 100644 --- a/src/arch/mod.rs +++ b/src/arch/mod.rs @@ -25,6 +25,7 @@ pub use aarch64::{AArch64 as ArchitectureImpl, ARCHITECTURE}; use device_api::{ interrupt::{ExternalInterruptController, IpiDeliveryTarget, LocalInterruptController}, timer::MonotonicTimestampProviderDevice, + ResetDevice, }; // cfg_if! { // if #[cfg(target_arch = "aarch64")] { @@ -106,6 +107,8 @@ pub trait Architecture { timer: &'static dyn MonotonicTimestampProviderDevice, ) -> Result<(), Error>; + fn register_reset_device(&self, reset: &'static dyn ResetDevice) -> Result<(), Error>; + // TODO only supports 1 extintc per system /// Returns the primary external interrupt controller fn external_interrupt_controller(&self) -> &'static dyn ExternalInterruptController; diff --git a/src/device/power/mod.rs b/src/device/power/mod.rs index 78fd5dc3..8b87a8c6 100644 --- a/src/device/power/mod.rs +++ b/src/device/power/mod.rs @@ -1,3 +1,4 @@ //! Power-management related device drivers pub mod arm_psci; +pub mod sunxi_rwdog; diff --git a/src/device/power/sunxi_rwdog.rs b/src/device/power/sunxi_rwdog.rs new file mode 100644 index 00000000..7d47e3de --- /dev/null +++ b/src/device/power/sunxi_rwdog.rs @@ -0,0 +1,110 @@ +//! Allwinner (H6) R Watchdog driver + +use abi::error::Error; +use alloc::boxed::Box; +use device_api::{Device, ResetDevice}; +use tock_registers::{ + interfaces::Writeable, register_bitfields, register_structs, registers::ReadWrite, +}; + +use crate::{ + arch::{ + aarch64::devtree::{self, DevTreeIndexNodePropGet, DevTreeIndexPropExt}, + Architecture, ARCHITECTURE, + }, + device_tree_driver, + mem::device::DeviceMemoryIo, + sync::IrqSafeSpinlock, + util::OneTimeInit, +}; + +register_bitfields! { + u32, + CTRL [ + KEY OFFSET(1) NUMBITS(12) [ + Value = 0xA57 + ], + RESTART OFFSET(0) NUMBITS(1) [] + ], + CFG [ + CONFIG OFFSET(0) NUMBITS(2) [ + System = 1, + ] + ], + MODE [ + EN OFFSET(0) NUMBITS(1) [], + ] +} + +register_structs! { + #[allow(non_snake_case)] + Regs { + (0x00 => IRQ_EN: ReadWrite), + (0x04 => IRQ_STA: ReadWrite), + (0x08 => _0), + (0x10 => CTRL: ReadWrite), + (0x14 => CFG: ReadWrite), + (0x18 => MODE: ReadWrite), + (0x1C => @END), + } +} + +struct Inner { + regs: DeviceMemoryIo, +} + +struct RWdog { + inner: OneTimeInit>, + base: usize, +} + +impl ResetDevice for RWdog { + unsafe fn reset(&self) -> ! { + // TODO disable IRQs + let inner = self.inner.get().lock(); + + inner.regs.CFG.write(CFG::CONFIG::System); + inner.regs.MODE.write(MODE::EN::SET); + inner.regs.CTRL.write(CTRL::KEY::Value + CTRL::RESTART::SET); + + loop { + core::arch::asm!("wfe"); + } + } +} + +impl Device for RWdog { + fn display_name(&self) -> &'static str { + "Allwinner H6 Watchdog" + } + + unsafe fn init(&'static self) -> Result<(), Error> { + let regs = DeviceMemoryIo::map("r_wdog", self.base)?; + + self.inner.init(IrqSafeSpinlock::new(Inner { regs })); + + ARCHITECTURE.register_reset_device(self)?; + + Ok(()) + } +} + +device_tree_driver! { + compatible: ["allwinner,sun50i-h6-wdt"], + probe(of) => { + let reg = devtree::find_prop(&of.node, "reg")?; + let status: &str = of.node.prop("status").unwrap_or("enabled"); + + if status == "disabled" { + return None; + } + + let (base, _) = reg.cell2_array_item(0, of.address_cells, of.size_cells)?; + let base = base as usize; + + Some(Box::new(RWdog { + inner: OneTimeInit::new(), + base + })) + } +} diff --git a/src/device/serial/sunxi_uart.rs b/src/device/serial/sunxi_uart.rs index 70d2c1b1..d1d24862 100644 --- a/src/device/serial/sunxi_uart.rs +++ b/src/device/serial/sunxi_uart.rs @@ -107,6 +107,10 @@ impl InterruptHandler for SunxiUart { let byte = inner.regs.DLL.get(); drop(inner); + if byte == b'\x1b' as u32 { + panic!("RESET TRIGGERED"); + } + self.recv_byte(byte as u8); } // inner.regs.ICR.write(ICR::ALL::CLEAR); diff --git a/src/panic.rs b/src/panic.rs index b3e3beab..ef3e9962 100644 --- a/src/panic.rs +++ b/src/panic.rs @@ -97,6 +97,11 @@ fn panic_handler(pi: &core::panic::PanicInfo) -> ! { log_print_raw!(LogLevel::Fatal, "X"); PANIC_FINISHED_FENCE.signal(); + + #[cfg(feature = "aarch64_orange_pi3")] + unsafe { + ARCHITECTURE.reset(); + } } loop {