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 <opensbi/extensions/pmu.h>
|
||||||
|
|
||||||
#include <sunxi_d1_pinctrl/sunxi_d1_pinctrl.h>
|
#include <sunxi_d1_pinctrl/sunxi_d1_pinctrl.h>
|
||||||
|
#include <uart_16550/uart_16550.h>
|
||||||
|
|
||||||
void fmt(const char *f, ...) {
|
void fmt(const char *f, ...) {
|
||||||
namespace legacy = drivers::opensbi::legacy;
|
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("imp id: {}\n", ret.value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fmt("uintmax: {} bits\n", sizeof(uintmax_t) * 8);
|
fmt("uintmax: {} bits\n", sizeof(uintmax_t) * 8);
|
||||||
fmt("HART: {}, DTB: {}\n", hart, dtb);
|
fmt("HART: {}, DTB: {}\n", hart, dtb);
|
||||||
|
|
||||||
|
// ==================
|
||||||
|
// == Pinctrl test ==
|
||||||
|
// ==================
|
||||||
namespace sunxi_d1_pinctrl = drivers::sunxi_d1_pinctrl;
|
namespace sunxi_d1_pinctrl = drivers::sunxi_d1_pinctrl;
|
||||||
sunxi_d1_pinctrl::SunxiD1Pinctrl pinctrl((void *)0x2000000);
|
sunxi_d1_pinctrl::SunxiD1Pinctrl pinctrl((void *)0x2000000);
|
||||||
|
|
||||||
pinctrl.set_pin_mode(sunxi_d1_pinctrl::Bank::PC, 1, 1);
|
pinctrl.set_pin_mode(sunxi_d1_pinctrl::Bank::PC, 1, 1);
|
||||||
pinctrl.set_pin_state(sunxi_d1_pinctrl::Bank::PC, 1, true);
|
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");
|
fmt("HALT\n");
|
||||||
while(1);
|
while(1);
|
||||||
|
|
||||||
namespace hsm = drivers::opensbi::hsm;
|
namespace hsm = drivers::opensbi::hsm;
|
||||||
hsm::hart_stop();
|
hsm::hart_stop();
|
||||||
|
|
||||||
while(1);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ kernel_cpp_args = []
|
||||||
kernel_link_args = ['-nostdlib']
|
kernel_link_args = ['-nostdlib']
|
||||||
|
|
||||||
subdir('drivers/opensbi')
|
subdir('drivers/opensbi')
|
||||||
|
subdir('drivers/uart_16550')
|
||||||
subdir('drivers/sunxi_d1_pinctrl')
|
subdir('drivers/sunxi_d1_pinctrl')
|
||||||
# subdir('common/dtb')
|
# subdir('common/dtb')
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue