summaryrefslogtreecommitdiff
path: root/Kernel/Prekernel
diff options
context:
space:
mode:
authorNico Weber <thakis@chromium.org>2021-09-26 14:10:10 -0400
committerBrian Gianforcaro <b.gianfo@gmail.com>2021-09-26 23:14:01 +0000
commit54aabb07f956c6f4ce7787bab20bc05c9a80931c (patch)
treebcec2a45712fd5b04bd5df6e0ce49369f4e622e3 /Kernel/Prekernel
parent44c787e88b5e0ea96920a1f01b49f9bc67b5dc48 (diff)
downloadserenity-54aabb07f956c6f4ce7787bab20bc05c9a80931c.zip
Kernel: Add UART class for aarch64
Diffstat (limited to 'Kernel/Prekernel')
-rw-r--r--Kernel/Prekernel/Arch/aarch64/UART.cpp162
-rw-r--r--Kernel/Prekernel/Arch/aarch64/UART.h51
-rw-r--r--Kernel/Prekernel/Arch/aarch64/init.cpp16
-rw-r--r--Kernel/Prekernel/CMakeLists.txt1
4 files changed, 224 insertions, 6 deletions
diff --git a/Kernel/Prekernel/Arch/aarch64/UART.cpp b/Kernel/Prekernel/Arch/aarch64/UART.cpp
new file mode 100644
index 0000000000..53596f0576
--- /dev/null
+++ b/Kernel/Prekernel/Arch/aarch64/UART.cpp
@@ -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)
+ ;
+}
+
+}
diff --git a/Kernel/Prekernel/Arch/aarch64/UART.h b/Kernel/Prekernel/Arch/aarch64/UART.h
new file mode 100644
index 0000000000..1533924b0e
--- /dev/null
+++ b/Kernel/Prekernel/Arch/aarch64/UART.h
@@ -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;
+};
+
+}
diff --git a/Kernel/Prekernel/Arch/aarch64/init.cpp b/Kernel/Prekernel/Arch/aarch64/init.cpp
index 3451b19fa8..f0833bf64e 100644
--- a/Kernel/Prekernel/Arch/aarch64/init.cpp
+++ b/Kernel/Prekernel/Arch/aarch64/init.cpp
@@ -5,21 +5,25 @@
*/
#include <AK/Types.h>
-#include <Kernel/Prekernel/Arch/aarch64/GPIO.h>
#include <Kernel/Prekernel/Arch/aarch64/Mailbox.h>
+#include <Kernel/Prekernel/Arch/aarch64/UART.h>
extern "C" [[noreturn]] void halt();
extern "C" [[noreturn]] void init();
extern "C" [[noreturn]] void init()
{
- auto& gpio = Prekernel::GPIO::the();
- gpio.set_pin_function(14, Prekernel::GPIO::PinFunction::Alternate0);
- gpio.set_pin_function(15, Prekernel::GPIO::PinFunction::Alternate0);
+ auto& uart = Prekernel::UART::the();
- 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();
}
diff --git a/Kernel/Prekernel/CMakeLists.txt b/Kernel/Prekernel/CMakeLists.txt
index 1583f253b4..952cbb3371 100644
--- a/Kernel/Prekernel/CMakeLists.txt
+++ b/Kernel/Prekernel/CMakeLists.txt
@@ -16,6 +16,7 @@ if ("${SERENITY_ARCH}" STREQUAL "aarch64")
Arch/aarch64/Mailbox.cpp
Arch/aarch64/MainIdRegister.cpp
Arch/aarch64/MMIO.cpp
+ Arch/aarch64/UART.cpp
Arch/aarch64/init.cpp
)
else()