mirror of
				https://github.com/RGBCube/serenity
				synced 2025-10-31 21:22:46 +00:00 
			
		
		
		
	Kernel: Add UART class for aarch64
This commit is contained in:
		
							parent
							
								
									44c787e88b
								
							
						
					
					
						commit
						54aabb07f9
					
				
					 4 changed files with 224 additions and 6 deletions
				
			
		
							
								
								
									
										162
									
								
								Kernel/Prekernel/Arch/aarch64/UART.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										162
									
								
								Kernel/Prekernel/Arch/aarch64/UART.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,162 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright (c) 2021, Nico Weber <thakis@chromium.org> | ||||||
|  |  * | ||||||
|  |  * SPDX-License-Identifier: BSD-2-Clause | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #include <Kernel/Prekernel/Arch/aarch64/GPIO.h> | ||||||
|  | #include <Kernel/Prekernel/Arch/aarch64/MMIO.h> | ||||||
|  | #include <Kernel/Prekernel/Arch/aarch64/Mailbox.h> | ||||||
|  | #include <Kernel/Prekernel/Arch/aarch64/UART.h> | ||||||
|  | 
 | ||||||
|  | namespace Prekernel { | ||||||
|  | 
 | ||||||
|  | // "13.4 Register View" / "11.5 Register View"
 | ||||||
|  | struct UARTRegisters { | ||||||
|  |     u32 data; | ||||||
|  |     u32 receive_status_or_error_clear; | ||||||
|  |     u32 unused[4]; | ||||||
|  |     u32 flag; | ||||||
|  |     u32 unused2; | ||||||
|  | 
 | ||||||
|  |     u32 unused_ilpr; | ||||||
|  |     u32 integer_baud_rate_divisor;    // Only the lowest 16 bits are used.
 | ||||||
|  |     u32 fractional_baud_rate_divisor; // Only the lowest 6 bits are used.
 | ||||||
|  |     u32 line_control; | ||||||
|  | 
 | ||||||
|  |     u32 control; | ||||||
|  |     u32 interrupt_fifo_level_select; | ||||||
|  |     u32 interrupt_mask_set_clear; | ||||||
|  |     u32 raw_interrupt_status; | ||||||
|  | 
 | ||||||
|  |     u32 masked_interrupt_status; | ||||||
|  |     u32 interrupt_clear; | ||||||
|  |     u32 dma_control; | ||||||
|  |     u32 test_control; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // Bits of the `flag` register.
 | ||||||
|  | // See "FR register" in Broadcom doc for details.
 | ||||||
|  | enum FlagBits { | ||||||
|  |     ClearToSend = 1 << 0, | ||||||
|  |     UnsupportedDSR = 1 << 1, | ||||||
|  |     UnsupportedDCD = 1 << 2, | ||||||
|  |     UARTBusy = 1 << 3, | ||||||
|  |     ReceiveFifoEmpty = 1 << 4, | ||||||
|  |     TransmitFifoFull = 1 << 5, | ||||||
|  |     ReceiveFifoFull = 1 << 6, | ||||||
|  |     TransmitFifoEmpty = 1 << 7, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // Bits for the `line_control` register.
 | ||||||
|  | // See "LCRH register" in Broadcom doc for details.
 | ||||||
|  | enum LineControlBits { | ||||||
|  |     SendBreak = 1 << 0, | ||||||
|  |     EnableParityCheckingAndGeneration = 1 << 1, | ||||||
|  |     EvenParity = 1 << 2, | ||||||
|  |     TransmitTwoStopBits = 1 << 3, | ||||||
|  |     EnableFIFOs = 1 << 4, | ||||||
|  | 
 | ||||||
|  |     WordLength5Bits = 0b00 << 5, | ||||||
|  |     WordLength6Bits = 0b01 << 5, | ||||||
|  |     WordLength7Bits = 0b10 << 5, | ||||||
|  |     WordLength8Bits = 0b11 << 5, | ||||||
|  | 
 | ||||||
|  |     StickParity = 1 << 7, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | // Bits for the `control` register.
 | ||||||
|  | // See "CR register" in Broadcom doc for details. From there:
 | ||||||
|  | //     NOTE: Program the control registers as follows:
 | ||||||
|  | //     1. Disable the UART.
 | ||||||
|  | //     2. Wait for the end of transmission or reception of the current character.
 | ||||||
|  | //     3. Flush the transmit FIFO by setting the FEN bit to 0 in the Line Control Register, UART_LCRH.
 | ||||||
|  | //     4. Reprogram the Control Register, UART_CR.
 | ||||||
|  | //     5. Enable the UART
 | ||||||
|  | enum ControlBits { | ||||||
|  |     UARTEnable = 1 << 0, | ||||||
|  |     UnsupportedSIREN = 1 << 1, | ||||||
|  |     UnsupportedSIRLP = 1 << 2, | ||||||
|  |     // Bits 3-6 are reserved.
 | ||||||
|  |     LoopbackEnable = 1 << 7, | ||||||
|  |     TransmitEnable = 1 << 8, | ||||||
|  |     ReceiveEnable = 1 << 9, | ||||||
|  |     UnsupportedDTR = 1 << 10, | ||||||
|  |     RequestToSend = 1 << 11, | ||||||
|  |     UnsupportedOut1 = 1 << 12, | ||||||
|  |     UnsupportedOut2 = 1 << 13, | ||||||
|  |     RTSHardwareFlowControlEnable = 1 << 14, | ||||||
|  |     CTSHardwareFlowControlEnable = 1 << 15, | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | UART::UART() | ||||||
|  |     : m_registers(MMIO::the().peripheral<UARTRegisters>(0x20'1000)) | ||||||
|  | { | ||||||
|  |     // Disable UART while changing configuration.
 | ||||||
|  |     m_registers->control = 0; | ||||||
|  | 
 | ||||||
|  |     // FIXME: Should wait for current transmission to end and should flush FIFO.
 | ||||||
|  | 
 | ||||||
|  |     constexpr int baud_rate = 115'200; | ||||||
|  | 
 | ||||||
|  |     // Set UART clock so that the baud rate divisor ends up as 1.0.
 | ||||||
|  |     // FIXME: Not sure if this is a good UART clock rate.
 | ||||||
|  |     u32 rate_in_hz = Mailbox::set_clock_rate(Mailbox::ClockID::UART, 16 * baud_rate); | ||||||
|  | 
 | ||||||
|  |     // The BCM's PL011 UART is alternate function 0 on pins 14 and 15.
 | ||||||
|  |     auto& gpio = Prekernel::GPIO::the(); | ||||||
|  |     gpio.set_pin_function(14, Prekernel::GPIO::PinFunction::Alternate0); | ||||||
|  |     gpio.set_pin_function(15, Prekernel::GPIO::PinFunction::Alternate0); | ||||||
|  |     gpio.set_pin_pull_up_down_state(Array { 14, 15 }, Prekernel::GPIO::PullUpDownState::Disable); | ||||||
|  | 
 | ||||||
|  |     // Clock and pins are configured. Turn UART on.
 | ||||||
|  |     set_baud_rate(baud_rate, rate_in_hz); | ||||||
|  |     m_registers->line_control = EnableFIFOs | WordLength8Bits; | ||||||
|  | 
 | ||||||
|  |     m_registers->control = UARTEnable | TransmitEnable | ReceiveEnable; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | UART& UART::the() | ||||||
|  | { | ||||||
|  |     static UART instance; | ||||||
|  |     return instance; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void UART::send(u32 c) | ||||||
|  | { | ||||||
|  |     wait_until_we_can_send(); | ||||||
|  |     m_registers->data = c; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | u32 UART::receive() | ||||||
|  | { | ||||||
|  |     wait_until_we_can_receive(); | ||||||
|  | 
 | ||||||
|  |     // Mask out error bits.
 | ||||||
|  |     return m_registers->data & 0xFF; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void UART::set_baud_rate(int baud_rate, int uart_frequency_in_hz) | ||||||
|  | { | ||||||
|  |     // Broadcom doc: """Baud rate divisor BAUDDIV = (FUARTCLK/(16 * Baud rate))""".
 | ||||||
|  |     // BAUDDIV is stored as a 16.6 fixed point value, so do computation scaled by (1 << 6) == 64.
 | ||||||
|  |     // 64*(FUARTCLK/(16 * Baud rate)) == 4*FUARTCLK/(Baud rate). For rounding, add 0.5 == (Baud rate/2)/(Baud rate).
 | ||||||
|  |     u32 baud_rate_divisor_fixed_point = (4 * uart_frequency_in_hz + baud_rate / 2) / baud_rate; | ||||||
|  | 
 | ||||||
|  |     m_registers->integer_baud_rate_divisor = baud_rate_divisor_fixed_point / 64; | ||||||
|  |     m_registers->fractional_baud_rate_divisor = baud_rate_divisor_fixed_point % 64; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void UART::wait_until_we_can_send() | ||||||
|  | { | ||||||
|  |     while (m_registers->flag & TransmitFifoFull) | ||||||
|  |         ; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | void UART::wait_until_we_can_receive() | ||||||
|  | { | ||||||
|  |     while (m_registers->flag & ReceiveFifoEmpty) | ||||||
|  |         ; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | } | ||||||
							
								
								
									
										51
									
								
								Kernel/Prekernel/Arch/aarch64/UART.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								Kernel/Prekernel/Arch/aarch64/UART.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,51 @@ | ||||||
|  | /*
 | ||||||
|  |  * Copyright (c) 2021, Nico Weber <thakis@chromium.org> | ||||||
|  |  * | ||||||
|  |  * SPDX-License-Identifier: BSD-2-Clause | ||||||
|  |  */ | ||||||
|  | 
 | ||||||
|  | #pragma once | ||||||
|  | 
 | ||||||
|  | #include <AK/Types.h> | ||||||
|  | 
 | ||||||
|  | namespace Prekernel { | ||||||
|  | 
 | ||||||
|  | struct UARTRegisters; | ||||||
|  | 
 | ||||||
|  | // Abstracts the PL011 UART on a Raspberry Pi.
 | ||||||
|  | // (The BCM2711 on a Raspberry Pi 4 has five PL011 UARTs; this is always the first of those.)
 | ||||||
|  | class UART { | ||||||
|  | public: | ||||||
|  |     static UART& the(); | ||||||
|  | 
 | ||||||
|  |     void send(u32 c); | ||||||
|  |     u32 receive(); | ||||||
|  | 
 | ||||||
|  |     void print_str(const char* s) | ||||||
|  |     { | ||||||
|  |         while (*s) | ||||||
|  |             send(*s++); | ||||||
|  |     } | ||||||
|  |     void print_num(u32 n) | ||||||
|  |     { | ||||||
|  |         char buf[11]; | ||||||
|  |         int i = 0; | ||||||
|  |         while (n) { | ||||||
|  |             buf[i++] = (n % 10) + '0'; | ||||||
|  |             n /= 10; | ||||||
|  |         } | ||||||
|  |         for (i--; i >= 0; i--) | ||||||
|  |             send(buf[i]); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  | private: | ||||||
|  |     UART(); | ||||||
|  | 
 | ||||||
|  |     void set_baud_rate(int baud_rate, int uart_frequency_in_hz); | ||||||
|  |     void wait_until_we_can_send(); | ||||||
|  |     void wait_until_we_can_receive(); | ||||||
|  | 
 | ||||||
|  |     UARTRegisters volatile* m_registers; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -5,21 +5,25 @@ | ||||||
|  */ |  */ | ||||||
| 
 | 
 | ||||||
| #include <AK/Types.h> | #include <AK/Types.h> | ||||||
| #include <Kernel/Prekernel/Arch/aarch64/GPIO.h> |  | ||||||
| #include <Kernel/Prekernel/Arch/aarch64/Mailbox.h> | #include <Kernel/Prekernel/Arch/aarch64/Mailbox.h> | ||||||
|  | #include <Kernel/Prekernel/Arch/aarch64/UART.h> | ||||||
| 
 | 
 | ||||||
| extern "C" [[noreturn]] void halt(); | extern "C" [[noreturn]] void halt(); | ||||||
| 
 | 
 | ||||||
| extern "C" [[noreturn]] void init(); | extern "C" [[noreturn]] void init(); | ||||||
| extern "C" [[noreturn]] void init() | extern "C" [[noreturn]] void init() | ||||||
| { | { | ||||||
|     auto& gpio = Prekernel::GPIO::the(); |     auto& uart = Prekernel::UART::the(); | ||||||
|     gpio.set_pin_function(14, Prekernel::GPIO::PinFunction::Alternate0); |  | ||||||
|     gpio.set_pin_function(15, Prekernel::GPIO::PinFunction::Alternate0); |  | ||||||
| 
 | 
 | ||||||
|     gpio.set_pin_pull_up_down_state(Array { 14, 15 }, Prekernel::GPIO::PullUpDownState::Disable); |     uart.print_str("\r\nWelcome to Serenity OS!\r\n"); | ||||||
|  |     uart.print_str("Imagine this being your ideal operating system.\r\n"); | ||||||
|  |     uart.print_str("Observed deviations from that ideal are shortcomings of your imagination.\r\n\r\n"); | ||||||
|  | 
 | ||||||
|  |     u32 firmware_version = Prekernel::Mailbox::query_firmware_version(); | ||||||
|  |     uart.print_str("Firmware version: "); | ||||||
|  |     uart.print_num(firmware_version); | ||||||
|  |     uart.print_str("\r\n"); | ||||||
| 
 | 
 | ||||||
|     [[maybe_unused]] u32 firmware_version = Prekernel::Mailbox::query_firmware_version(); |  | ||||||
|     halt(); |     halt(); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -16,6 +16,7 @@ if ("${SERENITY_ARCH}" STREQUAL "aarch64") | ||||||
|         Arch/aarch64/Mailbox.cpp |         Arch/aarch64/Mailbox.cpp | ||||||
|         Arch/aarch64/MainIdRegister.cpp |         Arch/aarch64/MainIdRegister.cpp | ||||||
|         Arch/aarch64/MMIO.cpp |         Arch/aarch64/MMIO.cpp | ||||||
|  |         Arch/aarch64/UART.cpp | ||||||
|         Arch/aarch64/init.cpp |         Arch/aarch64/init.cpp | ||||||
|     ) |     ) | ||||||
| else() | else() | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue
	
	 Nico Weber
						Nico Weber