sunxi-h6: reset through watchdog

This commit is contained in:
Mark Poliakov 2023-08-15 18:38:54 +03:00
parent 887d65f890
commit f6b27eb42f
7 changed files with 137 additions and 1 deletions

View File

@ -22,3 +22,7 @@ pub trait CpuBringupDevice: Device {
/// misused. /// misused.
unsafe fn start_cpu(&self, id: usize, ip: usize, arg0: usize) -> Result<(), Error>; unsafe fn start_cpu(&self, id: usize, ip: usize, arg0: usize) -> Result<(), Error>;
} }
pub trait ResetDevice: Device {
unsafe fn reset(&self) -> !;
}

View File

@ -15,6 +15,7 @@ use device_api::{
ExternalInterruptController, IpiDeliveryTarget, IrqNumber, LocalInterruptController, ExternalInterruptController, IpiDeliveryTarget, IrqNumber, LocalInterruptController,
}, },
timer::MonotonicTimestampProviderDevice, timer::MonotonicTimestampProviderDevice,
ResetDevice,
}; };
use fdt_rs::prelude::PropReader; use fdt_rs::prelude::PropReader;
use git_version::git_version; use git_version::git_version;
@ -70,6 +71,7 @@ pub struct AArch64 {
ext_intc: OneTimeInit<&'static dyn ExternalInterruptController>, ext_intc: OneTimeInit<&'static dyn ExternalInterruptController>,
local_intc: OneTimeInit<&'static dyn LocalInterruptController<IpiMessage = CpuMessage>>, local_intc: OneTimeInit<&'static dyn LocalInterruptController<IpiMessage = CpuMessage>>,
mtimer: OneTimeInit<&'static dyn MonotonicTimestampProviderDevice>, mtimer: OneTimeInit<&'static dyn MonotonicTimestampProviderDevice>,
reset: OneTimeInit<&'static dyn ResetDevice>,
// ARM-only devices // ARM-only devices
/// ARM PSCI instance on this system (there may not be one) /// ARM PSCI instance on this system (there may not be one)
@ -82,6 +84,7 @@ pub static ARCHITECTURE: AArch64 = AArch64 {
ext_intc: OneTimeInit::new(), ext_intc: OneTimeInit::new(),
local_intc: OneTimeInit::new(), local_intc: OneTimeInit::new(),
mtimer: OneTimeInit::new(), mtimer: OneTimeInit::new(),
reset: OneTimeInit::new(),
// ARM-only devices // ARM-only devices
psci: OneTimeInit::new(), psci: OneTimeInit::new(),
@ -180,6 +183,11 @@ impl Architecture for AArch64 {
Ok(()) 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 { fn external_interrupt_controller(&self) -> &'static dyn ExternalInterruptController {
*self.ext_intc.get() *self.ext_intc.get()
} }
@ -203,7 +211,8 @@ impl Architecture for AArch64 {
} }
unsafe fn reset(&self) -> ! { unsafe fn reset(&self) -> ! {
todo!() let reset = self.reset.get();
reset.reset();
} }
} }

View File

@ -25,6 +25,7 @@ pub use aarch64::{AArch64 as ArchitectureImpl, ARCHITECTURE};
use device_api::{ use device_api::{
interrupt::{ExternalInterruptController, IpiDeliveryTarget, LocalInterruptController}, interrupt::{ExternalInterruptController, IpiDeliveryTarget, LocalInterruptController},
timer::MonotonicTimestampProviderDevice, timer::MonotonicTimestampProviderDevice,
ResetDevice,
}; };
// cfg_if! { // cfg_if! {
// if #[cfg(target_arch = "aarch64")] { // if #[cfg(target_arch = "aarch64")] {
@ -106,6 +107,8 @@ pub trait Architecture {
timer: &'static dyn MonotonicTimestampProviderDevice, timer: &'static dyn MonotonicTimestampProviderDevice,
) -> Result<(), Error>; ) -> Result<(), Error>;
fn register_reset_device(&self, reset: &'static dyn ResetDevice) -> Result<(), Error>;
// TODO only supports 1 extintc per system // TODO only supports 1 extintc per system
/// Returns the primary external interrupt controller /// Returns the primary external interrupt controller
fn external_interrupt_controller(&self) -> &'static dyn ExternalInterruptController; fn external_interrupt_controller(&self) -> &'static dyn ExternalInterruptController;

View File

@ -1,3 +1,4 @@
//! Power-management related device drivers //! Power-management related device drivers
pub mod arm_psci; pub mod arm_psci;
pub mod sunxi_rwdog;

View File

@ -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<u32>),
(0x04 => IRQ_STA: ReadWrite<u32>),
(0x08 => _0),
(0x10 => CTRL: ReadWrite<u32, CTRL::Register>),
(0x14 => CFG: ReadWrite<u32, CFG::Register>),
(0x18 => MODE: ReadWrite<u32, MODE::Register>),
(0x1C => @END),
}
}
struct Inner {
regs: DeviceMemoryIo<Regs>,
}
struct RWdog {
inner: OneTimeInit<IrqSafeSpinlock<Inner>>,
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
}))
}
}

View File

@ -107,6 +107,10 @@ impl InterruptHandler for SunxiUart {
let byte = inner.regs.DLL.get(); let byte = inner.regs.DLL.get();
drop(inner); drop(inner);
if byte == b'\x1b' as u32 {
panic!("RESET TRIGGERED");
}
self.recv_byte(byte as u8); self.recv_byte(byte as u8);
} }
// inner.regs.ICR.write(ICR::ALL::CLEAR); // inner.regs.ICR.write(ICR::ALL::CLEAR);

View File

@ -97,6 +97,11 @@ fn panic_handler(pi: &core::panic::PanicInfo) -> ! {
log_print_raw!(LogLevel::Fatal, "X"); log_print_raw!(LogLevel::Fatal, "X");
PANIC_FINISHED_FENCE.signal(); PANIC_FINISHED_FENCE.signal();
#[cfg(feature = "aarch64_orange_pi3")]
unsafe {
ARCHITECTURE.reset();
}
} }
loop { loop {