mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 04:57:44 +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