diff --git a/kernel/src/arch/aarch64/boot/mod.rs b/kernel/src/arch/aarch64/boot/mod.rs index db46a27..1c3a8b4 100644 --- a/kernel/src/arch/aarch64/boot/mod.rs +++ b/kernel/src/arch/aarch64/boot/mod.rs @@ -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); } } diff --git a/kernel/src/arch/aarch64/boot/uboot.S b/kernel/src/arch/aarch64/boot/uboot.S index 8ba2611..d8971c6 100644 --- a/kernel/src/arch/aarch64/boot/uboot.S +++ b/kernel/src/arch/aarch64/boot/uboot.S @@ -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 diff --git a/kernel/src/arch/aarch64/mach_orangepi3/gpio.rs b/kernel/src/arch/aarch64/mach_orangepi3/gpio.rs new file mode 100644 index 0000000..9edfdb1 --- /dev/null +++ b/kernel/src/arch/aarch64/mach_orangepi3/gpio.rs @@ -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; 4]), + (0x10 => DAT: ReadWrite), + (0x14 => DRV: [ReadWrite; 2]), + (0x1C => PUL: [ReadWrite; 2]), + (0x24 => @END), + } +} + +pub(super) struct Gpio { + regs: MemoryIo, +} + +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 { + 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 { + 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), + } + } +} diff --git a/kernel/src/arch/aarch64/mach_orangepi3/mod.rs b/kernel/src/arch/aarch64/mach_orangepi3/mod.rs index 2ddb954..890523c 100644 --- a/kernel/src/arch/aarch64/mach_orangepi3/mod.rs +++ b/kernel/src/arch/aarch64/mach_orangepi3/mod.rs @@ -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 { - 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 { &UART0 } -///// Returns the timer used as CPU-local periodic IRQ source -//#[inline] -//pub fn local_timer() -> &'static Spin { -// &LOCAL_TIMER -//} +/// Returns the timer used as CPU-local periodic IRQ source +#[inline] +pub fn local_timer() -> &'static Spin { + &LOCAL_TIMER +} -static UART0: Spin = Spin::new(Uart { base: UART0_BASE }); +static UART0: Spin = Spin::new(unsafe { Uart::new(UART0_BASE) }); +static LOCAL_TIMER: Spin = Spin::new(GenericTimer {}); +#[allow(dead_code)] +static GPIOD: Spin = Spin::new(unsafe { Gpio::new(PIO_BASE + 0x24 * 3) }); +static GPIOH: Spin = Spin::new(unsafe { Gpio::new(PIO_BASE + 0x24 * 7) }); diff --git a/kernel/src/arch/aarch64/mach_orangepi3/uart.rs b/kernel/src/arch/aarch64/mach_orangepi3/uart.rs new file mode 100644 index 0000000..3421f47 --- /dev/null +++ b/kernel/src/arch/aarch64/mach_orangepi3/uart.rs @@ -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), + (0x0004 => IER_DLH: ReadWrite), + (0x0008 => IIR_FCR: Aliased), + (0x000C => LCR: ReadWrite), + (0x0010 => MCR: ReadWrite), + (0x0014 => LSR: ReadOnly), + (0x0018 => MSR: ReadOnly), + (0x001C => SCH: ReadWrite), + (0x0020 => _res0), + (0x007C => USR: ReadOnly), + (0x0080 => TFL: ReadWrite), + (0x0084 => RFL: ReadWrite), + (0x0088 => HSK: ReadWrite), + (0x008C => _res1), + (0x00A4 => HALT: ReadWrite), + (0x00D0 => @END), + } +} + +pub(super) struct Uart { + regs: MemoryIo, +} + +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 { + 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), + } + } +} diff --git a/kernel/src/arch/aarch64/mach_qemu/mod.rs b/kernel/src/arch/aarch64/mach_qemu/mod.rs index b95de4a..5c4d38a 100644 --- a/kernel/src/arch/aarch64/mach_qemu/mod.rs +++ b/kernel/src/arch/aarch64/mach_qemu/mod.rs @@ -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 { diff --git a/kernel/src/arch/aarch64/vectors.S b/kernel/src/arch/aarch64/vectors.S index 958a621..1b7e2d6 100644 --- a/kernel/src/arch/aarch64/vectors.S +++ b/kernel/src/arch/aarch64/vectors.S @@ -1,6 +1,6 @@ .section .text .global aa64_el1_vectors -.p2align 8 +.p2align 12 aa64_el1_vectors: .el1_sp_el0_sync: b . diff --git a/kernel/src/debug.rs b/kernel/src/debug.rs index a7d969d..e521fbe 100644 --- a/kernel/src/debug.rs +++ b/kernel/src/debug.rs @@ -15,6 +15,9 @@ impl fmt::Write for SerialOutput { 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(); } diff --git a/kernel/src/dev/gpio.rs b/kernel/src/dev/gpio.rs new file mode 100644 index 0000000..0dd649e --- /dev/null +++ b/kernel/src/dev/gpio.rs @@ -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; + + /// 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; +} + +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 + } + } +} diff --git a/kernel/src/dev/mod.rs b/kernel/src/dev/mod.rs index 5e17083..ba0d874 100644 --- a/kernel/src/dev/mod.rs +++ b/kernel/src/dev/mod.rs @@ -5,6 +5,7 @@ use error::Errno; // Device classes pub mod serial; pub mod timer; +pub mod gpio; /// Generic device trait pub trait Device {