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
This commit is contained in:
parent
259d929c13
commit
80dc438fd0
6 changed files with 314 additions and 3 deletions
161
kernel/drivers/uart_16550/include/uart_16550/registers.h
Normal file
161
kernel/drivers/uart_16550/include/uart_16550/registers.h
Normal file
|
@ -0,0 +1,161 @@
|
|||
#include <stdint.h>
|
||||
|
||||
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
|
116
kernel/drivers/uart_16550/include/uart_16550/uart_16550.h
Normal file
116
kernel/drivers/uart_16550/include/uart_16550/uart_16550.h
Normal file
|
@ -0,0 +1,116 @@
|
|||
#pragma once
|
||||
|
||||
#include "registers.h"
|
||||
#include <stdint.h>
|
||||
|
||||
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<Registers *>(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<volatile uint8_t &>(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
|
9
kernel/drivers/uart_16550/meson.build
Normal file
9
kernel/drivers/uart_16550/meson.build
Normal file
|
@ -0,0 +1,9 @@
|
|||
kernel_sources += [
|
||||
files(
|
||||
'uart_16550.cpp',
|
||||
),
|
||||
]
|
||||
|
||||
kernel_includes += include_directories(
|
||||
'include'
|
||||
)
|
8
kernel/drivers/uart_16550/uart_16550.cpp
Normal file
8
kernel/drivers/uart_16550/uart_16550.cpp
Normal file
|
@ -0,0 +1,8 @@
|
|||
#include "uart_16550/uart_16550.h"
|
||||
|
||||
namespace drivers {
|
||||
namespace uart_16550 {
|
||||
|
||||
|
||||
} // End namespace uart_16550
|
||||
} // End namespace drivers
|
|
@ -14,6 +14,7 @@
|
|||
#include <opensbi/extensions/pmu.h>
|
||||
|
||||
#include <sunxi_d1_pinctrl/sunxi_d1_pinctrl.h>
|
||||
#include <uart_16550/uart_16550.h>
|
||||
|
||||
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);
|
||||
}
|
||||
|
|
|
@ -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')
|
||||
|
||||
|
|
Loading…
Reference in a new issue