- 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
73 lines
2.1 KiB
C++
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 ®(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
|