feat: mach_orangepi3 feature parity with mach_qemu

This commit is contained in:
Mark Poliakov 2021-10-06 14:04:31 +03:00
parent e21d7fdd59
commit ea7eb300f7
10 changed files with 382 additions and 85 deletions

View File

@ -1,10 +1,10 @@
//! aarch64 common boot logic
use crate::arch::aarch64::asm::{CPACR_EL1};
use cortex_a::registers::{VBAR_EL1, SCTLR_EL1};
use tock_registers::interfaces::{Writeable, ReadWriteable};
use crate::arch::{aarch64::asm::CPACR_EL1, machine};
use crate::dev::{Device, serial::SerialDevice, timer::TimestampSource};
use cortex_a::asm::barrier::{self, dsb, isb};
use cortex_a::registers::{SCTLR_EL1, VBAR_EL1};
use tock_registers::interfaces::{ReadWriteable, Writeable};
#[no_mangle]
fn __aa64_bsp_main() {
@ -21,44 +21,25 @@ fn __aa64_bsp_main() {
dsb(barrier::SY);
isb(barrier::SY);
SCTLR_EL1.modify(SCTLR_EL1::I::SET +
SCTLR_EL1::SA::SET +
SCTLR_EL1::C::SET +
SCTLR_EL1::A::SET);
SCTLR_EL1
.modify(SCTLR_EL1::I::SET + SCTLR_EL1::SA::SET + SCTLR_EL1::C::SET + SCTLR_EL1::A::SET);
dsb(barrier::SY);
isb(barrier::SY);
}
debugln!("Test");
machine::init_board().unwrap();
let mut el: u64;
let mut sctlr_el1: u64;
unsafe {
asm!("mrs {}, currentel", out(reg) el);
asm!("mrs {}, sctlr_el1", out(reg) sctlr_el1);
machine::local_timer().lock().enable().unwrap();
}
el >>= 2;
el &= 0x3;
debugln!("Current EL = {}", el);
debugln!("SCTLR_EL1 = {:#x}", sctlr_el1);
//use crate::arch::machine;
//use crate::dev::{serial::SerialDevice, timer::TimestampSource, Device};
//unsafe {
//machine::console().lock().enable().unwrap();
//machine::local_timer().lock().enable().unwrap();
//}
//let base = machine::local_timer().lock().timestamp().unwrap();
let base = machine::local_timer().lock().timestamp().unwrap();
loop {
cortex_a::asm::wfe();
//let count = machine::local_timer().lock().timestamp().unwrap();
//let ch = machine::console().lock().recv(true).unwrap();
//debugln!("[{:?}] {:#04x} = '{}'!", count - base, ch, ch as char);
let count = machine::local_timer().lock().timestamp().unwrap();
let ch = machine::console().lock().recv(true).unwrap();
debugln!("[{:?}] {:#04x} = '{}'!", count - base, ch, ch as char);
}
}

View File

@ -1,16 +1,25 @@
// vi:ft=a64asm.asm:
.set UART0_BASE, 0x05000000
.set SCTLR_EL2_RES1, 0x30C50830
.set SPSR_EL2_EL1h, 0x5
.set HCR_EL2_RW, 1 << 31
.set HCR_EL2_HCD, 1 << 29
.set SPSR_EL2_EL1h, 0x5
.set SPSR_EL2_MASK_DAIF, 0xF << 6
.set HCR_EL2_RW, 1 << 31
.set HCR_EL2_HCD, 1 << 29
.set CNTHCTL_EL2_EL1PCEN, 1 << 1
.set CNTHCTL_EL2_EL1PCTEN, 1 << 0
.macro ADR_REL reg, sym
adrp \reg, \sym
add \reg, \reg, #:lo12:\sym
.endm
.macro MOV_L reg, value
mov \reg, #((\value) & 0xFFFF)
movk \reg, #((\value) >> 16), lsl #16
.endm
.section .text._entry
.global _entry
_entry:
@ -19,16 +28,27 @@ _entry:
lsr x0, x0, #2
cmp x0, #2
bne 1f
// Exit EL2
// TODO cnthtctl_el2 setup
mrs x0, cnthctl_el2
orr x0, x0, #(CNTHCTL_EL2_EL1PCTEN | CNTHCTL_EL2_EL1PCEN)
msr cnthctl_el2, x0
msr cntvoff_el2, xzr
MOV_L x0, SCTLR_EL2_RES1
msr sctlr_el2, x0
mov x0, #HCR_EL2_RW
msr hcr_el2, x0
mov x0, #SPSR_EL2_EL1h
orr x0, x0, #SPSR_EL2_MASK_DAIF
msr spsr_el2, x0
ADR_REL x0, 1f
msr elr_el2, x0
mov x0, #SPSR_EL2_EL1h
msr spsr_el2, x0
mov x0, #(HCR_EL2_RW | HCR_EL2_HCD)
msr hcr_el2, x0
isb
eret
1:
dsb sy
@ -37,7 +57,9 @@ _entry:
ADR_REL x0, bsp_stack_top
mov sp, x0
b __aa64_bsp_main
mov lr, xzr
bl __aa64_bsp_main
b .
.section .bss
.p2align 12

View File

@ -0,0 +1,111 @@
use crate::arch::MemoryIo;
use crate::dev::{
gpio::{GpioDevice, PinConfig, PinMode, PullMode},
Device,
};
use error::Errno;
use tock_registers::interfaces::{Readable, Writeable};
use tock_registers::register_structs;
use tock_registers::registers::ReadWrite;
pub const PH0_UART0_TX: u32 = 2;
pub const PH1_UART0_RX: u32 = 2;
register_structs! {
#[allow(non_snake_case)]
Regs {
(0x00 => CFG: [ReadWrite<u32>; 4]),
(0x10 => DAT: ReadWrite<u32>),
(0x14 => DRV: [ReadWrite<u32>; 2]),
(0x1C => PUL: [ReadWrite<u32>; 2]),
(0x24 => @END),
}
}
pub(super) struct Gpio {
regs: MemoryIo<Regs>,
}
impl Device for Gpio {
fn name() -> &'static str {
"Allwinner H6 GPIO Controller"
}
unsafe fn enable(&mut self) -> Result<(), Errno> {
Ok(())
}
}
impl GpioDevice for Gpio {
unsafe fn set_pin_config(&mut self, pin: u32, cfg: &PinConfig) -> Result<(), Errno> {
let pull = match cfg.pull {
PullMode::None => 0,
PullMode::Up => 1,
PullMode::Down => 2,
};
match cfg.mode {
PinMode::Disable => self.set_pin_cfg_inner(pin, 7),
PinMode::Input => {
self.set_pin_cfg_inner(pin, 0);
self.set_pin_pul_inner(pin, pull);
}
PinMode::Output => {
self.set_pin_cfg_inner(pin, 1); // TODO is it the same for all pins?
self.set_pin_pul_inner(pin, pull);
}
PinMode::InputInterrupt => {
todo!()
}
PinMode::Alt => {
assert!(cfg.func > 1 && cfg.func < 7);
self.set_pin_cfg_inner(pin, cfg.func);
}
}
Ok(())
}
unsafe fn get_pin_config(&mut self, _pin: u32) -> Result<PinConfig, Errno> {
todo!()
}
fn set_pin(&mut self, pin: u32) {
self.regs.DAT.set(self.regs.DAT.get() | (1 << pin));
}
fn clear_pin(&mut self, pin: u32) {
self.regs.DAT.set(self.regs.DAT.get() & !(1 << pin));
}
fn toggle_pin(&mut self, pin: u32) {
self.regs.DAT.set(self.regs.DAT.get() ^ (1 << pin));
}
fn read_pin(&mut self, pin: u32) -> Result<bool, Errno> {
Ok(self.regs.DAT.get() & (1 << pin) != 0)
}
}
impl Gpio {
#[inline]
fn set_pin_cfg_inner(&mut self, pin: u32, cfg: u32) {
let reg = pin >> 3;
let shift = (pin & 0x7) * 4;
let tmp = self.regs.CFG[reg as usize].get() & !(0xF << shift);
self.regs.CFG[reg as usize].set(tmp | ((cfg & 0x7) << shift));
}
#[inline]
fn set_pin_pul_inner(&mut self, pin: u32, pul: u32) {
let reg = pin >> 4;
let shift = (pin & 0xF) * 2;
let tmp = self.regs.PUL[reg as usize].get() & !(0x3 << shift);
self.regs.PUL[reg as usize].set(tmp | ((pul & 0x3) << shift));
}
pub const unsafe fn new(base: usize) -> Self {
Self {
regs: MemoryIo::new(base),
}
}
}

View File

@ -1,51 +1,35 @@
//! QEMU virt machine
//! Xunlong Orange Pi 3, with Allwinner H6 SoC
use crate::arch::aarch64::timer::GenericTimer;
use crate::dev::{Device, serial::SerialDevice};
use crate::dev::timer::TimestampSource;
use crate::dev::{
gpio::{GpioDevice, PinConfig},
serial::SerialDevice,
timer::TimestampSource,
Device,
};
use crate::sync::Spin;
use error::Errno;
fn delay(mut p: usize) {
while p != 0 {
cortex_a::asm::nop();
p -= 1;
}
}
mod gpio;
mod uart;
struct Uart {
base: usize
}
use gpio::Gpio;
use uart::Uart;
impl Device for Uart {
fn name() -> &'static str {
"Allwinner H6 UART"
}
#[allow(missing_docs)]
pub fn init_board() -> Result<(), Errno> {
unsafe {
let mut gpioh = GPIOH.lock();
gpioh.set_pin_config(0, &PinConfig::alt(gpio::PH0_UART0_TX))?;
gpioh.set_pin_config(1, &PinConfig::alt(gpio::PH1_UART0_RX))?;
unsafe fn enable(&mut self) -> Result<(), Errno> {
todo!()
}
}
impl SerialDevice for Uart {
fn send(&mut self, byte: u8) -> Result<(), Errno> {
unsafe {
if byte == b'\n' {
core::ptr::write_volatile(self.base as *mut u32, 13u32);
delay(10000);
}
core::ptr::write_volatile(self.base as *mut u32, byte as u32);
delay(10000);
}
Ok(())
}
fn recv(&mut self, blocking: bool) -> Result<u8, Errno> {
todo!()
UART0.lock().enable()?;
}
Ok(())
}
const UART0_BASE: usize = 0x05000000;
const PIO_BASE: usize = 0x0300B000;
/// Returns primary console for this machine
#[inline]
@ -53,10 +37,14 @@ pub fn console() -> &'static Spin<impl SerialDevice> {
&UART0
}
///// Returns the timer used as CPU-local periodic IRQ source
//#[inline]
//pub fn local_timer() -> &'static Spin<impl TimestampSource> {
// &LOCAL_TIMER
//}
/// Returns the timer used as CPU-local periodic IRQ source
#[inline]
pub fn local_timer() -> &'static Spin<impl TimestampSource> {
&LOCAL_TIMER
}
static UART0: Spin<Uart> = Spin::new(Uart { base: UART0_BASE });
static UART0: Spin<Uart> = Spin::new(unsafe { Uart::new(UART0_BASE) });
static LOCAL_TIMER: Spin<GenericTimer> = Spin::new(GenericTimer {});
#[allow(dead_code)]
static GPIOD: Spin<Gpio> = Spin::new(unsafe { Gpio::new(PIO_BASE + 0x24 * 3) });
static GPIOH: Spin<Gpio> = Spin::new(unsafe { Gpio::new(PIO_BASE + 0x24 * 7) });

View File

@ -0,0 +1,106 @@
use crate::arch::MemoryIo;
use crate::dev::{serial::SerialDevice, Device};
use error::Errno;
use tock_registers::registers::{Aliased, ReadOnly, ReadWrite};
use tock_registers::interfaces::{Readable, Writeable};
use tock_registers::{register_bitfields, register_structs};
register_bitfields! [
u32,
IER [
PTIME OFFSET(7) NUMBITS(1) [],
RS485_INT_EN OFFSET(4) NUMBITS(1) [],
EDSSI OFFSET(3) NUMBITS(1) [],
ELSI OFFSET(2) NUMBITS(1) [],
ETBEI OFFSET(1) NUMBITS(1) [],
ERBFI OFFSET(0) NUMBITS(1) [],
],
IIR [
FEFLAG OFFSET(6) NUMBITS(2) [
Enable = 3,
Disable = 0
],
IID OFFSET(0) NUMBITS(4) [
ModemStatus = 0,
NoInterrupt = 1,
ThrEmpty = 2,
Rs485Interrupt = 3,
ReceivedDataAvailable = 4,
ReceiverLineStatus = 6,
BusyDetect = 7,
CharacterTimeout = 12
]
],
LSR [
FIFOERR OFFSET(7) NUMBITS(1) [],
TEMT OFFSET(6) NUMBITS(1) [],
THRE OFFSET(5) NUMBITS(1) [],
BI OFFSET(4) NUMBITS(1) [],
FE OFFSET(3) NUMBITS(1) [],
PE OFFSET(2) NUMBITS(1) [],
OE OFFSET(1) NUMBITS(1) [],
DR OFFSET(0) NUMBITS(1) []
]
];
register_structs! {
#[allow(non_snake_case)]
Regs {
(0x0000 => DR_DLL: Aliased<u32>),
(0x0004 => IER_DLH: ReadWrite<u32, IER::Register>),
(0x0008 => IIR_FCR: Aliased<u32, IIR::Register, ()>),
(0x000C => LCR: ReadWrite<u32>),
(0x0010 => MCR: ReadWrite<u32>),
(0x0014 => LSR: ReadOnly<u32, LSR::Register>),
(0x0018 => MSR: ReadOnly<u32>),
(0x001C => SCH: ReadWrite<u32>),
(0x0020 => _res0),
(0x007C => USR: ReadOnly<u32>),
(0x0080 => TFL: ReadWrite<u32>),
(0x0084 => RFL: ReadWrite<u32>),
(0x0088 => HSK: ReadWrite<u32>),
(0x008C => _res1),
(0x00A4 => HALT: ReadWrite<u32>),
(0x00D0 => @END),
}
}
pub(super) struct Uart {
regs: MemoryIo<Regs>,
}
impl Device for Uart {
fn name() -> &'static str {
"Allwinner H6 UART"
}
unsafe fn enable(&mut self) -> Result<(), Errno> {
// TODO
Ok(())
}
}
impl SerialDevice for Uart {
fn send(&mut self, byte: u8) -> Result<(), Errno> {
while !self.regs.LSR.matches_all(LSR::THRE::SET) {
cortex_a::asm::nop();
}
self.regs.DR_DLL.set(byte as u32);
Ok(())
}
fn recv(&mut self, _blocking: bool) -> Result<u8, Errno> {
while !self.regs.LSR.matches_all(LSR::DR::SET) {
cortex_a::asm::nop();
}
Ok(self.regs.DR_DLL.get() as u8)
}
}
impl Uart {
pub const unsafe fn new(base: usize) -> Self {
Self {
regs: MemoryIo::new(base),
}
}
}

View File

@ -1,12 +1,21 @@
//! QEMU virt machine
use crate::arch::aarch64::timer::GenericTimer;
use crate::dev::serial::{pl011::Pl011, SerialDevice};
use crate::dev::{Device, serial::{pl011::Pl011, SerialDevice}};
use crate::dev::timer::TimestampSource;
use crate::sync::Spin;
use error::Errno;
const UART0_BASE: usize = 0x09000000;
#[allow(missing_docs)]
pub fn init_board() -> Result<(), Errno> {
unsafe {
UART0.lock().enable()?;
}
Ok(())
}
/// Returns primary console for this machine
#[inline]
pub fn console() -> &'static Spin<impl SerialDevice> {

View File

@ -1,6 +1,6 @@
.section .text
.global aa64_el1_vectors
.p2align 8
.p2align 12
aa64_el1_vectors:
.el1_sp_el0_sync:
b .

View File

@ -15,6 +15,9 @@ impl<T: SerialDevice> fmt::Write for SerialOutput<T> {
fn write_str(&mut self, s: &str) -> fmt::Result {
let mut lock = self.inner.lock();
for &byte in s.as_bytes() {
if byte == b'\n' {
lock.send(b'\r').ok();
}
// TODO check for errors
lock.send(byte).ok();
}

76
kernel/src/dev/gpio.rs Normal file
View File

@ -0,0 +1,76 @@
//! GPIO and pin control interfaces
use crate::dev::Device;
use error::Errno;
/// Pin function mode
pub enum PinMode {
/// Do not use pin
Disable = 0,
/// Use pin as a GPIO input
Input,
/// Use pin as a GPIO output
Output,
/// Use pin as an external interrupt trigger source
InputInterrupt,
/// Use pin for peripheral functionality
Alt,
}
/// Input/output pin pull mode
pub enum PullMode {
/// No pull
None,
/// Pull up
Up,
/// Pull down
Down,
}
/// Pin configuration for [GpioDevice::set_pin_config]
pub struct PinConfig {
/// Pin function
pub mode: PinMode,
/// Pin pull mode, only used for Input/Output pins
pub pull: PullMode,
/// Alternate pin function, only used when mode == [PinMode::Alt]
pub func: u32,
}
// TODO separate traits for "single port controller" and "global gpio controller"
/// Generic GPIO controller interface
pub trait GpioDevice: Device {
/// Initializes configuration for given pin
unsafe fn set_pin_config(&mut self, pin: u32, cfg: &PinConfig) -> Result<(), Errno>;
/// Returns current configuration of given pin
unsafe fn get_pin_config(&mut self, pin: u32) -> Result<PinConfig, Errno>;
/// Sets `pin` to HIGH state
fn set_pin(&mut self, pin: u32);
/// Sets `pin` to LOW state
fn clear_pin(&mut self, pin: u32);
/// Toggles `pin`'s HIGH/LOW state
fn toggle_pin(&mut self, pin: u32);
/// Returns `true` if input `pin` is in HIGH state
fn read_pin(&mut self, pin: u32) -> Result<bool, Errno>;
}
impl PinConfig {
/// Alternative (peripheral) pin configuration
pub const fn alt(func: u32) -> Self {
Self {
mode: PinMode::Alt,
pull: PullMode::None,
func
}
}
/// Pull-down output
pub const fn out_pull_down() -> Self {
Self {
mode: PinMode::Output,
pull: PullMode::Down,
func: 0
}
}
}

View File

@ -5,6 +5,7 @@ use error::Errno;
// Device classes
pub mod serial;
pub mod timer;
pub mod gpio;
/// Generic device trait
pub trait Device {