feat: Enable interrupts for PL011 UART

This commit is contained in:
Berkus Decker 2023-08-06 17:35:24 +03:00 committed by Berkus Decker
parent 0ef9ca0dc6
commit 0d70caa271
2 changed files with 129 additions and 19 deletions

View File

@ -13,6 +13,7 @@ use {
console::interface,
cpu::loop_while,
devices::serial::SerialOps,
exception,
platform::{
device_driver::{common::MMIODerefWrapper, gpio, IRQNumber},
mailbox::{self, Mailbox, MailboxOps},
@ -151,15 +152,56 @@ register_bitfields! {
]
],
/// Interupt Clear Register
ICR [
/// Meta field for all pending interrupts
ALL OFFSET(0) NUMBITS(11) []
/// Interrupt FIFO Level Select Register.
IFLS [
/// Receive interrupt FIFO level select.
/// The trigger points for the receive interrupt are as follows.
RXIFLSEL OFFSET(3) NUMBITS(5) [
OneEigth = 0b000,
OneQuarter = 0b001,
OneHalf = 0b010,
ThreeQuarters = 0b011,
SevenEights = 0b100
]
],
/// Interupt Mask Set/Clear Register
/// Interrupt Mask Set/Clear Register.
IMSC [
/// Meta field for all interrupts
/// Receive timeout interrupt mask. A read returns the current mask for the UARTRTINTR
/// interrupt.
///
/// - On a write of 1, the mask of the UARTRTINTR interrupt is set.
/// - A write of 0 clears the mask.
RTIM OFFSET(6) NUMBITS(1) [
Disabled = 0,
Enabled = 1
],
/// Receive interrupt mask. A read returns the current mask for the UARTRXINTR interrupt.
///
/// - On a write of 1, the mask of the UARTRXINTR interrupt is set.
/// - A write of 0 clears the mask.
RXIM OFFSET(4) NUMBITS(1) [
Disabled = 0,
Enabled = 1
]
],
/// Masked Interrupt Status Register.
MIS [
/// Receive timeout masked interrupt status. Returns the masked interrupt state of the
/// UARTRTINTR interrupt.
RTMIS OFFSET(6) NUMBITS(1) [],
/// Receive masked interrupt status. Returns the masked interrupt state of the UARTRXINTR
/// interrupt.
RXMIS OFFSET(4) NUMBITS(1) []
],
/// Interrupt Clear Register
ICR [
/// Meta field for all pending interrupts
/// On a write of 1, the corresponding interrupt is cleared. A write of 0 has no effect.
ALL OFFSET(0) NUMBITS(11) []
],
@ -192,10 +234,10 @@ register_structs! {
(0x28 => FractionalBaudRate: WriteOnly<u32, FBRD::Register>),
(0x2c => LineControl: ReadWrite<u32, LCR_H::Register>), // @todo write-only?
(0x30 => Control: WriteOnly<u32, CR::Register>),
(0x34 => InterruptFifoLevelSelect: ReadWrite<u32>),
(0x34 => InterruptFifoLevelSelect: ReadWrite<u32, IFLS::Register>),
(0x38 => InterruptMaskSetClear: ReadWrite<u32, IMSC::Register>),
(0x3c => RawInterruptStatus: ReadOnly<u32>),
(0x40 => MaskedInterruptStatus: ReadOnly<u32>),
(0x40 => MaskedInterruptStatus: ReadOnly<u32, MIS::Register>),
(0x44 => InterruptClear: WriteOnly<u32, ICR::Register>),
(0x48 => DmaControl: WriteOnly<u32, DMACR::Register>),
(0x4c => __reserved_3),
@ -240,6 +282,13 @@ pub struct RateDivisors {
fractional_baud_rate_divisor: u32,
}
// [temporary] Used in mmu.rs to set up local paging
pub const UART0_BASE: usize = BcmHost::get_peripheral_address() + 0x20_1000;
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
impl RateDivisors {
// Set integer & fractional part of baud rate.
// Integer = clock/(16 * Baud)
@ -272,13 +321,6 @@ impl RateDivisors {
}
}
// [temporary] Used in mmu.rs to set up local paging
pub const UART0_BASE: usize = BcmHost::get_peripheral_address() + 0x20_1000;
//--------------------------------------------------------------------------------------------------
// Public Code
//--------------------------------------------------------------------------------------------------
impl PL011Uart {
pub const COMPATIBLE: &'static str = "BCM PL011 UART";
@ -377,8 +419,15 @@ impl PL011UartInner {
+ LCR_H::Stop2::Disabled,
);
// Mask all interrupts by setting corresponding bits to 1
self.registers.InterruptMaskSetClear.write(IMSC::ALL::SET);
// Set RX FIFO fill level at 1/8.
self.registers
.InterruptFifoLevelSelect
.write(IFLS::RXIFLSEL::OneEigth);
// Enable RX IRQ + RX timeout IRQ.
self.registers
.InterruptMaskSetClear
.write(IMSC::RXIM::Enabled + IMSC::RTIM::Enabled);
// Disable DMA
self.registers
@ -470,6 +519,20 @@ impl crate::drivers::interface::DeviceDriver for PL011Uart {
unsafe fn init(&self) -> core::result::Result<(), &'static str> {
self.inner.lock(|inner| inner.prepare())
}
fn register_and_enable_irq_handler(
&'static self,
irq_number: &Self::IRQNumberType,
) -> Result<(), &'static str> {
use exception::asynchronous::{irq_manager, IRQHandlerDescriptor};
let descriptor = IRQHandlerDescriptor::new(*irq_number, Self::COMPATIBLE, self);
irq_manager().register_handler(descriptor)?;
irq_manager().enable(irq_number);
Ok(())
}
}
impl SerialOps for PL011Uart {
@ -506,6 +569,29 @@ impl interface::ConsoleOps for PL011Uart {
impl interface::All for PL011Uart {}
impl exception::asynchronous::interface::IRQHandler for PL011Uart {
fn handle(&self) -> Result<(), &'static str> {
use interface::ConsoleOps;
self.inner.lock(|inner| {
let pending = inner.registers.MaskedInterruptStatus.extract();
// Clear all pending IRQs.
inner.registers.InterruptClear.write(ICR::ALL::SET);
// Check for any kind of RX interrupt.
if pending.matches_any(MIS::RXMIS::SET + MIS::RTMIS::SET) {
// Echo any received characters.
// while let Some(c) = inner.read_char() {
// inner.write_char(c)
// }
}
});
Ok(())
}
}
//--------------------------------------------------------------------------------------------------
// Testing
//--------------------------------------------------------------------------------------------------

View File

@ -1,6 +1,8 @@
use {
super::exception,
crate::{
console, drivers,
exception::{self as generic_exception},
platform::{device_driver, memory::map::mmio},
},
core::sync::atomic::{AtomicBool, Ordering},
@ -35,6 +37,7 @@ pub unsafe fn init() -> Result<(), &'static str> {
driver_gpio()?;
#[cfg(not(feature = "noserial"))]
driver_uart()?;
driver_interrupt_controller()?;
INIT_DONE.store(true, Ordering::Relaxed);
Ok(())
@ -90,13 +93,23 @@ fn post_init_gpio() -> Result<(), &'static str> {
Ok(())
}
/// This must be called only after successful init of the interrupt controller driver.
fn post_init_interrupt_controller() -> Result<(), &'static str> {
generic_exception::asynchronous::register_irq_manager(&INTERRUPT_CONTROLLER);
Ok(())
}
fn driver_uart() -> Result<(), &'static str> {
// let uart_descriptor =
// drivers::DeviceDriverDescriptor::new(&MINI_UART, Some(post_init_mini_uart));
// drivers::driver_manager().register_driver(uart_descriptor);
let uart_descriptor =
drivers::DeviceDriverDescriptor::new(&PL011_UART, Some(post_init_pl011_uart), None);
let uart_descriptor = drivers::DeviceDriverDescriptor::new(
&PL011_UART,
Some(post_init_pl011_uart),
Some(exception::asynchronous::irq_map::PL011_UART),
);
drivers::driver_manager().register_driver(uart_descriptor);
Ok(())
@ -108,3 +121,14 @@ fn driver_gpio() -> Result<(), &'static str> {
Ok(())
}
fn driver_interrupt_controller() -> Result<(), &'static str> {
let interrupt_controller_descriptor = drivers::DeviceDriverDescriptor::new(
&INTERRUPT_CONTROLLER,
Some(post_init_interrupt_controller),
None,
);
drivers::driver_manager().register_driver(interrupt_controller_descriptor);
Ok(())
}