From 80dc438fd00bea71fe9ce33d83b29db6ec88c4aa Mon Sep 17 00:00:00 2001 From: Quantum Date: Sun, 31 Jul 2022 23:29:24 -0400 Subject: [PATCH] Drivers: Added start of 16550 UART driver Seems to work well enough to take over the OpenSBI putchar TODO: Add some kind of flush() to avoid trashing data still in the FIFO when we take over from OpenSBI --- .../uart_16550/include/uart_16550/registers.h | 161 ++++++++++++++++++ .../include/uart_16550/uart_16550.h | 116 +++++++++++++ kernel/drivers/uart_16550/meson.build | 9 + kernel/drivers/uart_16550/uart_16550.cpp | 8 + kernel/kernel.cpp | 22 ++- kernel/meson.build | 1 + 6 files changed, 314 insertions(+), 3 deletions(-) create mode 100644 kernel/drivers/uart_16550/include/uart_16550/registers.h create mode 100644 kernel/drivers/uart_16550/include/uart_16550/uart_16550.h create mode 100644 kernel/drivers/uart_16550/meson.build create mode 100644 kernel/drivers/uart_16550/uart_16550.cpp diff --git a/kernel/drivers/uart_16550/include/uart_16550/registers.h b/kernel/drivers/uart_16550/include/uart_16550/registers.h new file mode 100644 index 0000000..c8be7ec --- /dev/null +++ b/kernel/drivers/uart_16550/include/uart_16550/registers.h @@ -0,0 +1,161 @@ +#include + +namespace drivers { +namespace uart_16550 { + +struct [[gnu::packed]] IER { + uint8_t data_ready : 1; + uint8_t thr_empty : 1; + uint8_t receiver_line_status : 1; + uint8_t modem_status : 1; + uint8_t : 2; + uint8_t dma_rx_end : 1; + uint8_t dma_tx_end : 1; +}; + +struct [[gnu::packed]]ISR { + uint8_t status : 1; + uint8_t fifos_enabled : 2; + uint8_t dma_tx_end : 1; + uint8_t dma_rx_end : 1; + uint8_t id_code : 3; +}; + +struct [[gnu::packed]] FCR { + uint8_t fifo_enable : 1; + uint8_t rx_fifo_reset : 1; + uint8_t tx_fifo_reset : 1; + uint8_t dma_mode : 1; + uint8_t enable_dma_end : 1; + uint8_t : 1; + uint8_t rx_fifo_trigger_level : 2; +}; + +struct [[gnu::packed]] LCR { + uint8_t word_length : 2; + uint8_t stop_bits : 1; + uint8_t parity_enable : 1; + uint8_t even_parity : 1; + uint8_t force_parity : 1; + uint8_t set_break : 1; + uint8_t dlab : 1; +}; + +struct [[gnu::packed]] MCR { + uint8_t dtr : 1; + uint8_t rts : 1; + uint8_t out_1 : 1; + uint8_t out2_int_enable : 1; + uint8_t loopback : 1; + uint8_t : 3; +}; + +struct [[gnu::packed]] LSR { + uint8_t data_ready : 1; + uint8_t overrun : 1; + uint8_t parity : 1; + uint8_t framing : 1; + uint8_t break_interrupt : 1; + uint8_t thr_empty : 1; + uint8_t tx_empty : 1; + uint8_t fifo_data_error : 1; +}; + +struct [[gnu::packed]] PSD { + uint8_t prescaler_division_factor: 4; + uint8_t : 4; +}; + +struct [[gnu::packed]] MSR { + uint8_t delta_cts : 1; + uint8_t delta_dsr : 1; + uint8_t trailing_edge_ri : 1; + uint8_t delta_cd : 1; + uint8_t cts : 1; + uint8_t dsr : 1; + uint8_t ri : 1; + uint8_t cd : 1; +}; + +// TEMP +#define UART_16550_32_BIT +#ifdef UART_16550_32_BIT + +struct [[gnu::packed]] Registers { + // 0x00 + union { + uint8_t rhr; + uint8_t thr; + uint8_t dcl; + }; + uint32_t : 24; + // 0x04 + union { + IER ier; + uint8_t dch; + }; + uint32_t : 24; + // 0x08 + union { + ISR isr; + FCR fcr; + }; + uint32_t : 24; + // 0x0C + LCR lcr; + uint32_t : 24; + // 0x10 + MCR mcr; + uint32_t : 24; + // 0x14 + union { + LSR lsr; + PSD psd; + }; + uint32_t : 24; + // 0x18 + MSR msr; + uint32_t : 24; + // 0x1C + uint8_t spr; + uint32_t : 24; +}; + +#else + +struct [[gnu::packed]] Registers { + // 0x00 + union { + uint8_t rhr; + uint8_t thr; + uint8_t dcl; + }; + // 0x01 + union { + IER ier; + uint8_t dch; + }; + // 0x02 + union { + ISR isr; + FCR fcr; + }; + // 0x03 + LCR lcr; + // 0x04 + MCR mcr; + // 0x05 + union { + LSR lsr; + PSD psd; + }; + // 0x06 + MSR msr; + // 0x07 + uint8_t spr; +}; + +#endif + +} // End namespace uart_16550 +} // End namespace drivers diff --git a/kernel/drivers/uart_16550/include/uart_16550/uart_16550.h b/kernel/drivers/uart_16550/include/uart_16550/uart_16550.h new file mode 100644 index 0000000..ca08c06 --- /dev/null +++ b/kernel/drivers/uart_16550/include/uart_16550/uart_16550.h @@ -0,0 +1,116 @@ +#pragma once + +#include "registers.h" +#include + +namespace drivers { +namespace uart_16550 { + +enum class DataBits { + BITS_5, + BITS_6, + BITS_7, + BITS_8, +}; + +enum class StopBits { + STOP_1, + STOP_1_5, + STOP_2, +}; + +enum class Parity { + NONE, + EVEN, + ODD, +}; + +struct Uart16550 { + Uart16550(void * const addr) : + m_registers(*reinterpret_cast(addr)) { + } + + void init( + const int baud_rate, + const DataBits data_bits, + const Parity parity, + const StopBits stop_bits) const volatile { + switch(data_bits) { + case DataBits::BITS_5: + m_registers.lcr.word_length = 0; + break; + case DataBits::BITS_6: + m_registers.lcr.word_length = 1; + break; + case DataBits::BITS_7: + m_registers.lcr.word_length = 2; + break; + case DataBits::BITS_8: + m_registers.lcr.word_length = 3; + break; + } + + switch(parity) { + case Parity::NONE: + m_registers.lcr.parity_enable = 0; + break; + case Parity::EVEN: + m_registers.lcr.parity_enable = 1; + m_registers.lcr.even_parity = 1; + break; + case Parity::ODD: + m_registers.lcr.parity_enable = 1; + m_registers.lcr.even_parity = 0; + break; + } + + switch(stop_bits) { + case StopBits::STOP_1: + m_registers.lcr.stop_bits = 0; + break; + case StopBits::STOP_1_5: + // TODO: Throw error if data_bits isnt BITS_5 + m_registers.lcr.stop_bits = 0; + break; + case StopBits::STOP_2: + // TODO: Throw error if data_bits is BITS_5 + m_registers.lcr.stop_bits = 1; + break; + } + + // Assert DLAB to allow setting the divisor + // TODO: Assert CHCFG_AT_BUSY (UART_HALT[1])? + m_registers.lcr.dlab = 1; + + // TODO: Get actual clock from TCC + const uint16_t divisor = 24000000 / baud_rate / 16; + m_registers.dch = divisor >> 8; + m_registers.dcl = divisor & 0xFF; + + // TODO: Assert CHANGE_UPDATE (UART_HALT[2])? + // TODO: Wait for CHANGE_UPDATE (UART_HALT[2]) to be deasserted? + + // Deassert DLAB to return to normal operation + m_registers.lcr.dlab = 0; + + // Disable interrupts + // TODO: Move to its own function + // TODO: Cheese? + reinterpret_cast(m_registers.ier) = 0; + + // Enable FIFO + // TODO: Move to its own function + m_registers.fcr.fifo_enable = 1; + } + + void write(const char c) { + while(m_registers.lsr.thr_empty == 0); + m_registers.thr = c; + } + + private: + volatile Registers &m_registers; +}; + +} // End namespace uart_16550 +} // End namespace drivers diff --git a/kernel/drivers/uart_16550/meson.build b/kernel/drivers/uart_16550/meson.build new file mode 100644 index 0000000..df96b15 --- /dev/null +++ b/kernel/drivers/uart_16550/meson.build @@ -0,0 +1,9 @@ +kernel_sources += [ + files( + 'uart_16550.cpp', + ), +] + +kernel_includes += include_directories( + 'include' +) diff --git a/kernel/drivers/uart_16550/uart_16550.cpp b/kernel/drivers/uart_16550/uart_16550.cpp new file mode 100644 index 0000000..c9781b3 --- /dev/null +++ b/kernel/drivers/uart_16550/uart_16550.cpp @@ -0,0 +1,8 @@ +#include "uart_16550/uart_16550.h" + +namespace drivers { +namespace uart_16550 { + + +} // End namespace uart_16550 +} // End namespace drivers diff --git a/kernel/kernel.cpp b/kernel/kernel.cpp index 856f366..47532fa 100644 --- a/kernel/kernel.cpp +++ b/kernel/kernel.cpp @@ -14,6 +14,7 @@ #include #include +#include void fmt(const char *f, ...) { namespace legacy = drivers::opensbi::legacy; @@ -107,21 +108,36 @@ extern "C" void kmain(unsigned long hart, void *dtb) { fmt("imp id: {}\n", ret.value); } - fmt("uintmax: {} bits\n", sizeof(uintmax_t) * 8); fmt("HART: {}, DTB: {}\n", hart, dtb); + // ================== + // == Pinctrl test == + // ================== namespace sunxi_d1_pinctrl = drivers::sunxi_d1_pinctrl; sunxi_d1_pinctrl::SunxiD1Pinctrl pinctrl((void *)0x2000000); pinctrl.set_pin_mode(sunxi_d1_pinctrl::Bank::PC, 1, 1); pinctrl.set_pin_state(sunxi_d1_pinctrl::Bank::PC, 1, true); + // =============== + // == UART test == + // =============== + namespace uart_16550 = drivers::uart_16550; + uart_16550::Uart16550 uart((void *)0x02500000); + // uart_16550::Uart16550 uart((void *)0x02500400); + uart.init(115200, uart_16550::DataBits::BITS_8, uart_16550::Parity::NONE, uart_16550::StopBits::STOP_1); + + while(true) { + uart.write('X'); + uart.write('\n'); + uint32_t a = 0; + for(a = 0; a < 0x10000000; a++); + } + fmt("HALT\n"); while(1); namespace hsm = drivers::opensbi::hsm; hsm::hart_stop(); - - while(1); } diff --git a/kernel/meson.build b/kernel/meson.build index 667e199..32d57d1 100644 --- a/kernel/meson.build +++ b/kernel/meson.build @@ -13,6 +13,7 @@ kernel_cpp_args = [] kernel_link_args = ['-nostdlib'] subdir('drivers/opensbi') +subdir('drivers/uart_16550') subdir('drivers/sunxi_d1_pinctrl') # subdir('common/dtb')