vesper/machine/src/platform/rpi3/power.rs

122 lines
3.4 KiB
Rust

/*
* SPDX-License-Identifier: MIT OR BlueOak-1.0.0
* Copyright (c) 2018-2019 Andre Richter <andre.o.richter@gmail.com>
* Copyright (c) Berkus Decker <berkus+vesper@metta.systems>
* Original code distributed under MIT, additional changes are under BlueOak-1.0.0
*/
use {
super::{
gpio,
mailbox::{channel, Mailbox, MailboxOps},
BcmHost,
},
crate::platform::MMIODerefWrapper,
snafu::Snafu,
tock_registers::{
interfaces::{Readable, Writeable},
register_structs,
registers::ReadWrite,
},
};
register_structs! {
#[allow(non_snake_case)]
RegisterBlock {
(0x00 => __reserved_1),
(0x1c => PM_RSTC: ReadWrite<u32>),
(0x20 => PM_RSTS: ReadWrite<u32>),
(0x24 => PM_WDOG: ReadWrite<u32>),
(0x28 => @END),
}
}
const PM_PASSWORD: u32 = 0x5a00_0000;
const PM_RSTC_WRCFG_CLR: u32 = 0xffff_ffcf;
const PM_RSTC_WRCFG_FULL_RESET: u32 = 0x0000_0020;
// The Raspberry Pi firmware uses the RSTS register to know which
// partition to boot from. The partition value is spread into bits 0, 2,
// 4, 6, 8, 10. Partition 63 is a special partition used by the
// firmware to indicate halt.
const PM_RSTS_RASPBERRYPI_HALT: u32 = 0x555;
const POWER_STATE_OFF: u32 = 0;
const POWER_STATE_ON: u32 = 1;
const POWER_STATE_DO_NOT_WAIT: u32 = 0;
const POWER_STATE_WAIT: u32 = 2;
#[derive(Debug, Snafu)]
pub enum PowerError {
#[snafu(display("Power setup failed in mailbox operation"))]
MailboxError,
}
pub type Result<T> = ::core::result::Result<T, PowerError>;
type Registers = MMIODerefWrapper<RegisterBlock>;
const POWER_START: usize = 0x0010_0000;
/// Public interface to the Power subsystem
pub struct Power {
registers: Registers,
}
impl Default for Power {
fn default() -> Power {
const POWER_BASE: usize = BcmHost::get_peripheral_address() + POWER_START;
unsafe { Power::new(POWER_BASE) }
}
}
impl Power {
/// # Safety
///
/// Unsafe, duh!
pub const unsafe fn new(base_addr: usize) -> Power {
Power {
registers: Registers::new(base_addr),
}
}
/// Shutdown the board
pub fn off(&self, gpio: &gpio::GPIO) -> Result<()> {
// power off devices one by one
for dev_id in 0..16 {
let mut mbox = Mailbox::<8>::default();
let index = mbox.request();
let index =
mbox.set_device_power(index, dev_id, POWER_STATE_OFF | POWER_STATE_DO_NOT_WAIT);
let mbox = mbox.end(index);
mbox.call(channel::PropertyTagsArmToVc)
.map_err(|_| PowerError::MailboxError)?;
}
gpio.power_off();
// We set the watchdog hard reset bit here to distinguish this
// reset from the normal (full) reset. bootcode.bin will not
// reboot after a hard reset.
let mut val = self.registers.PM_RSTS.get();
val |= PM_PASSWORD | PM_RSTS_RASPBERRYPI_HALT;
self.registers.PM_RSTS.set(val);
// Continue with normal reset mechanism
self.reset();
}
/// Reboot
pub fn reset(&self) -> ! {
// use a timeout of 10 ticks (~150us)
self.registers.PM_WDOG.set(PM_PASSWORD | 10);
let mut val = self.registers.PM_RSTC.get();
val &= PM_RSTC_WRCFG_CLR;
val |= PM_PASSWORD | PM_RSTC_WRCFG_FULL_RESET;
self.registers.PM_RSTC.set(val);
crate::endless_sleep()
}
}