diff --git a/machine/src/platform/raspberrypi/device_driver/bcm/pl011_uart.rs b/machine/src/platform/raspberrypi/device_driver/bcm/pl011_uart.rs index d36af65..5f97418 100644 --- a/machine/src/platform/raspberrypi/device_driver/bcm/pl011_uart.rs +++ b/machine/src/platform/raspberrypi/device_driver/bcm/pl011_uart.rs @@ -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), (0x2c => LineControl: ReadWrite), // @todo write-only? (0x30 => Control: WriteOnly), - (0x34 => InterruptFifoLevelSelect: ReadWrite), + (0x34 => InterruptFifoLevelSelect: ReadWrite), (0x38 => InterruptMaskSetClear: ReadWrite), (0x3c => RawInterruptStatus: ReadOnly), - (0x40 => MaskedInterruptStatus: ReadOnly), + (0x40 => MaskedInterruptStatus: ReadOnly), (0x44 => InterruptClear: WriteOnly), (0x48 => DmaControl: WriteOnly), (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 //-------------------------------------------------------------------------------------------------- diff --git a/machine/src/platform/raspberrypi/drivers.rs b/machine/src/platform/raspberrypi/drivers.rs index e4e730d..19b0d08 100644 --- a/machine/src/platform/raspberrypi/drivers.rs +++ b/machine/src/platform/raspberrypi/drivers.rs @@ -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(()) +}