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, console::interface,
cpu::loop_while, cpu::loop_while,
devices::serial::SerialOps, devices::serial::SerialOps,
exception,
platform::{ platform::{
device_driver::{common::MMIODerefWrapper, gpio, IRQNumber}, device_driver::{common::MMIODerefWrapper, gpio, IRQNumber},
mailbox::{self, Mailbox, MailboxOps}, mailbox::{self, Mailbox, MailboxOps},
@ -151,15 +152,56 @@ register_bitfields! {
] ]
], ],
/// Interupt Clear Register /// Interrupt FIFO Level Select Register.
ICR [ IFLS [
/// Meta field for all pending interrupts /// Receive interrupt FIFO level select.
ALL OFFSET(0) NUMBITS(11) [] /// 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 [ 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) [] ALL OFFSET(0) NUMBITS(11) []
], ],
@ -192,10 +234,10 @@ register_structs! {
(0x28 => FractionalBaudRate: WriteOnly<u32, FBRD::Register>), (0x28 => FractionalBaudRate: WriteOnly<u32, FBRD::Register>),
(0x2c => LineControl: ReadWrite<u32, LCR_H::Register>), // @todo write-only? (0x2c => LineControl: ReadWrite<u32, LCR_H::Register>), // @todo write-only?
(0x30 => Control: WriteOnly<u32, CR::Register>), (0x30 => Control: WriteOnly<u32, CR::Register>),
(0x34 => InterruptFifoLevelSelect: ReadWrite<u32>), (0x34 => InterruptFifoLevelSelect: ReadWrite<u32, IFLS::Register>),
(0x38 => InterruptMaskSetClear: ReadWrite<u32, IMSC::Register>), (0x38 => InterruptMaskSetClear: ReadWrite<u32, IMSC::Register>),
(0x3c => RawInterruptStatus: ReadOnly<u32>), (0x3c => RawInterruptStatus: ReadOnly<u32>),
(0x40 => MaskedInterruptStatus: ReadOnly<u32>), (0x40 => MaskedInterruptStatus: ReadOnly<u32, MIS::Register>),
(0x44 => InterruptClear: WriteOnly<u32, ICR::Register>), (0x44 => InterruptClear: WriteOnly<u32, ICR::Register>),
(0x48 => DmaControl: WriteOnly<u32, DMACR::Register>), (0x48 => DmaControl: WriteOnly<u32, DMACR::Register>),
(0x4c => __reserved_3), (0x4c => __reserved_3),
@ -240,6 +282,13 @@ pub struct RateDivisors {
fractional_baud_rate_divisor: u32, 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 { impl RateDivisors {
// Set integer & fractional part of baud rate. // Set integer & fractional part of baud rate.
// Integer = clock/(16 * Baud) // 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 { impl PL011Uart {
pub const COMPATIBLE: &'static str = "BCM PL011 UART"; pub const COMPATIBLE: &'static str = "BCM PL011 UART";
@ -377,8 +419,15 @@ impl PL011UartInner {
+ LCR_H::Stop2::Disabled, + LCR_H::Stop2::Disabled,
); );
// Mask all interrupts by setting corresponding bits to 1 // Set RX FIFO fill level at 1/8.
self.registers.InterruptMaskSetClear.write(IMSC::ALL::SET); 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 // Disable DMA
self.registers self.registers
@ -470,6 +519,20 @@ impl crate::drivers::interface::DeviceDriver for PL011Uart {
unsafe fn init(&self) -> core::result::Result<(), &'static str> { unsafe fn init(&self) -> core::result::Result<(), &'static str> {
self.inner.lock(|inner| inner.prepare()) 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 { impl SerialOps for PL011Uart {
@ -506,6 +569,29 @@ impl interface::ConsoleOps for PL011Uart {
impl interface::All 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 // Testing
//-------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------

View File

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