Add support for 16550 serial controller

Signed-off-by: Harry Chen <i@harrychen.xyz>
master
Harry Chen 6 years ago
parent 2d5b6948b5
commit 4e3b2fb80f

@ -1,6 +1,7 @@
use core::fmt::{Write, Result, Arguments}; use core::fmt::{Write, Result, Arguments};
use core::ptr::{read_volatile, write_volatile};
struct SerialPort; struct SerialPort {};
impl Write for SerialPort { impl Write for SerialPort {
fn write_str(&mut self, s: &str) -> Result { fn write_str(&mut self, s: &str) -> Result {
@ -17,26 +18,40 @@ impl Write for SerialPort {
} }
} }
fn write<T>(addr: usize, content: T) {
let cell = (addr) as *mut T;
write_volatile(cell, content);
}
fn read<T>(addr: usize) -> T {
let cell = (addr) as *const T;
read_volatile(cell);
}
/// non-blocking version of putchar()
fn putchar(c: u8) { fn putchar(c: u8) {
// TODO: output to uart write(COM1 + COM_TX, c);
} }
/// blocking version of getchar()
pub fn getchar() -> char { pub fn getchar() -> char {
// TODO: get char from uart loop {
let c = 0 as u8; if (read::<u8>(COM1 + COM_LSR) & COM_LSR_DATA) == 0 {
break;
}
}
let c = read::<u8>(COM1 + COM_RX);
match c { match c {
255 => '\0', // null 255 => '\0', // null
c => c as char, c => c as char,
} }
} }
/// non-blocking version of getchar()
pub fn getchar_option() -> Option<char> { pub fn getchar_option() -> Option<char> {
// TODO: get char from uart match read::<u8>(COM1 + COM_LSR) & COM_LSR_DATA {
let c = 0 as u8; 0 => None,
match c { else => Some(read::<u8>(COM1 + COM_RX) as u8 as char),
-1 => None,
c => Some(c as u8 as char),
} }
} }
@ -44,5 +59,42 @@ pub fn putfmt(fmt: Arguments) {
SerialPort.write_fmt(fmt).unwrap(); SerialPort.write_fmt(fmt).unwrap();
} }
const TXDATA: *mut u32 = 0x38000000 as *mut u32; pub fn init(serial_base_addr: usize) {
const RXDATA: *mut u32 = 0x38000004 as *mut u32; COM1 = serial_base_addr;
// Turn off the FIFO
write(COM1 + COM_FCR, 0 as u8);
// Set speed; requires DLAB latch
write(COM1 + COM_LCR, COM_LCR_DLAB);
write(COM1 + COM_DLL, (115200 / 9600) as u8);
write(COM1 + COM_DLM, 0 as u8);
// 8 data bits, 1 stop bit, parity off; turn off DLAB latch
write(COM1 + COM_LCR, COM_LCR_WLEN8 & !COM_LCR_DLAB);
// No modem controls
write(COM1 + COM_MCR, 0 as u8);
// Enable rcv interrupts
write(COM1 + COM_IER, COM_IER_RDI);
}
static mut COM1: usize = 0;
const COM_RX :usize = 0; // In: Receive buffer (DLAB=0)
const COM_TX :usize = 0; // Out: Transmit buffer (DLAB=0)
const COM_DLL :usize = 0; // Out: Divisor Latch Low (DLAB=1)
const COM_DLM :usize = 1; // Out: Divisor Latch High (DLAB=1)
const COM_IER :usize = 1; // Out: Interrupt Enable Register
const COM_IER_RDI :u8 = 0x01; // Enable receiver data interrupt
const COM_IIR :usize = 2; // In: Interrupt ID Register
const COM_FCR :usize = 2; // Out: FIFO Control Register
const COM_LCR :usize = 3; // Out: Line Control Register
const COM_LCR_DLAB :u8 = 0x80; // Divisor latch access bit
const COM_LCR_WLEN8 :u8 = 0x03; // Wordlength: 8 bits
const COM_MCR :usize = 4; // Out: Modem Control Register
const COM_MCR_RTS :u8 = 0x02; // RTS complement
const COM_MCR_DTR :u8 = 0x01; // DTR complement
const COM_MCR_OUT2 :u8 = 0x08; // Out2 complement
const COM_LSR :usize = 5; // In: Line Status Register
const COM_LSR_DATA :u8 = 0x01; // Data available
const COM_LSR_TXRDY :u8 = 0x20; // Transmit buffer avail
const COM_LSR_TSRE :u8 = 0x40; // Transmitter off

@ -38,6 +38,8 @@ pub extern fn rust_main() -> ! {
unsafe { memory::clear_bss(); } unsafe { memory::clear_bss(); }
crate::io::init();
println!("Hello MIPS 32 from CPU {}, dtb @ {:#x}", cpu_id, dtb_start); println!("Hello MIPS 32 from CPU {}, dtb @ {:#x}", cpu_id, dtb_start);
crate::logging::init(); crate::logging::init();

Loading…
Cancel
Save