hal_riscv/hw/
uart16550.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
// SPDX-License-Identifier: MPL-2.0
// Copyright 2022, Isaac Woods

use bit_field::BitField as _;
use hal::memory::VAddr;
use volatile::Volatile;

/// The register block of a UART16550-compatible serial device. The usage of the registers are
/// explained well [here](https://www.lammertbies.nl/comm/info/serial-uart).
#[repr(C)]
pub struct Registers<R> {
    data: Volatile<R>,
    interrupt_enable: Volatile<R>,
    interrupt_identity: Volatile<R>,
    line_control: Volatile<R>,
    modem_control: Volatile<R>,
    line_status: Volatile<R>,
    modem_status: Volatile<R>,
    scratch: Volatile<R>,
}

pub enum Uart16550<'a> {
    One(&'a mut Registers<u8>),
    Four(&'a mut Registers<u32>),
}

impl<'a> Uart16550<'a> {
    pub unsafe fn new(addr: VAddr, reg_width: usize) -> Uart16550<'a> {
        match reg_width {
            1 => Self::One(unsafe { &mut *(addr.mut_ptr() as *mut Registers<u8>) }),
            4 => Self::Four(unsafe { &mut *(addr.mut_ptr() as *mut Registers<u32>) }),
            _ => panic!("Unsupported register width!"),
        }
    }

    pub fn init(&self) {
        // TODO: repeating this is pretty not great. Rewrite register definitions to make reg width
        // problems go away without doing this
        match self {
            Self::One(registers) => {
                // 8 data bits
                registers.line_control.write(0x03);
                // Clear pending interrupt (if any)
                registers.interrupt_identity.write(0x01);
                // Interrupt on data received
                registers.interrupt_enable.write(0x01);

                // Setting bit 7 of LCR exposes the DLL and DLM registers
                let line_control = registers.line_control.read();
                registers.line_control.write(line_control | (1 << 7));
                // Set a baud rate of 115200 (DLL=0x01, DLM=0x00)
                registers.data.write(0x01);
                registers.interrupt_enable.write(0x00);
                registers.line_control.write(line_control);
            }
            Self::Four(registers) => {
                // 8 data bits
                registers.line_control.write(0x03);
                // Clear pending interrupt (if any), no FIFOs, no modem status changes
                registers.interrupt_identity.write(0x01);
                // Interrupt on data received
                registers.interrupt_enable.write(0x01);

                // Setting bit 7 of LCR exposes the DLL and DLM registers
                let line_control = registers.line_control.read();
                registers.line_control.write(line_control | (1 << 7));
                // Set a baud rate of 115200 (DLL=0x01, DLM=0x00)
                registers.data.write(0x01);
                registers.interrupt_enable.write(0x00);
                registers.line_control.write(line_control);
            }
        }
    }

    fn line_status(&self) -> u8 {
        match self {
            Self::One(registers) => registers.line_status.read(),
            Self::Four(registers) => registers.line_status.read() as u8,
        }
    }

    pub fn write(&self, data: u8) {
        while !self.line_status().get_bit(5) {}
        match self {
            Self::One(registers) => registers.data.write(data),
            Self::Four(registers) => registers.data.write(data as u32),
        }
    }

    pub fn read(&self) -> Option<u8> {
        if self.line_status().get_bit(0) {
            match self {
                Self::One(registers) => Some(registers.data.read()),
                Self::Four(registers) => Some(registers.data.read() as u8),
            }
        } else {
            None
        }
    }
}

impl<'a> core::fmt::Write for Uart16550<'a> {
    fn write_str(&mut self, s: &str) -> core::fmt::Result {
        for byte in s.bytes() {
            self.write(byte);
        }
        Ok(())
    }
}