universalisos/kernel/arch/arm/uart.cpp
Fábio Coutada bafa872415 refactor(kernel): rewrite Stage 1 kernel in C++
- Rename kernel.c -> kernel.cpp and uart.c -> uart.cpp
- Add universalisos::uart namespace with constexpr register definitions
- Use extern "C" linkage for kernel_main() called from boot.S
- Compile with arm-none-eabi-g++ using C++17 freestanding flags
  (-fno-exceptions, -fno-rtti, -fno-threadsafe-statics, -fno-use-cxa-atexit)
- Update Makefile to use g++ and .cpp build rules
2026-07-06 22:53:50 +01:00

73 lines
2.1 KiB
C++

/*
* PL011 UART driver for QEMU ARM virt machine.
*
* The virt machine exposes a PL011 UART at 0x09000000.
*/
#include "uart.h"
namespace universalisos::uart {
namespace {
constexpr unsigned int UART0_BASE = 0x09000000U;
constexpr unsigned int REG_DR = 0x00U; // Data Register
constexpr unsigned int REG_FR = 0x18U; // Flag Register
constexpr unsigned int REG_IBRD = 0x24U; // Integer Baud Rate Divisor
constexpr unsigned int REG_FBRD = 0x28U; // Fractional Baud Rate Divisor
constexpr unsigned int REG_LCR_H = 0x2CU; // Line Control Register
constexpr unsigned int REG_CR = 0x30U; // Control Register
constexpr unsigned int REG_IMSC = 0x38U; // Interrupt Mask Set/Clear
constexpr unsigned int REG_ICR = 0x44U; // Interrupt Clear Register
constexpr unsigned int FR_TXFF = 1U << 5; // Transmit FIFO full
constexpr unsigned int CR_UARTEN = 1U << 0; // UART enable
constexpr unsigned int CR_TXE = 1U << 8; // Transmit enable
constexpr unsigned int CR_RXE = 1U << 9; // Receive enable
constexpr unsigned int LCR_H_WLEN_8 = 3U << 5; // 8 data bits
constexpr unsigned int LCR_H_FEN = 1U << 4; // Enable FIFOs
inline volatile unsigned int &reg(unsigned int offset)
{
return *reinterpret_cast<volatile unsigned int *>(UART0_BASE + offset);
}
} // namespace
void init()
{
// Disable UART while configuring.
reg(REG_CR) = 0U;
// Clear pending interrupts.
reg(REG_ICR) = 0x7FFU;
// 8 data bits, no parity, one stop bit, FIFO enabled.
reg(REG_LCR_H) = LCR_H_WLEN_8 | LCR_H_FEN;
// Baud rate divisor for 115200 with a 24 MHz UARTCLK.
// QEMU ignores the actual baud value, but a valid divisor is required.
reg(REG_IBRD) = 13U;
reg(REG_FBRD) = 1U;
// Enable UART, TX and RX.
reg(REG_CR) = CR_UARTEN | CR_TXE | CR_RXE;
}
void putc(char c)
{
while ((reg(REG_FR) & FR_TXFF) != 0U) {
// Wait until the transmit FIFO is not full.
}
reg(REG_DR) = static_cast<unsigned int>(c);
}
void puts(const char *s)
{
while (*s != '\0') {
putc(*s++);
}
}
} // namespace universalisos::uart