any_uart/
lib.rs

1#![cfg_attr(not(test), no_std)]
2
3use core::{
4    ptr::NonNull,
5    sync::atomic::{Ordering, fence},
6};
7
8#[cfg(feature = "alloc")]
9extern crate alloc;
10
11#[cfg(feature = "alloc")]
12mod api;
13
14pub use core::fmt::Write;
15pub use embedded_hal_nb::nb::block;
16pub use embedded_hal_nb::serial::ErrorKind;
17
18use aux_mini::AuxMini;
19pub use fdt_parser::Node;
20use fdt_parser::{Chosen, Fdt};
21use ns16550::Ns16550;
22use pl011::Pl011;
23
24mod aux_mini;
25mod ns16550;
26mod pl011;
27
28pub type Error = embedded_hal_nb::nb::Error<ErrorKind>;
29pub type FnPhysToVirt = fn(usize) -> *mut u8;
30
31pub struct Uart {
32    data: UartData,
33    pub tx: Option<Sender>,
34    pub rx: Option<Receiver>,
35    op: UartOp,
36}
37
38impl Uart {
39    fn _new<C: Console>(data: UartData) -> Self {
40        let op = C::to_op();
41        C::open(data);
42
43        Self {
44            data,
45            tx: Some(Sender { uart: data, op }),
46            rx: Some(Receiver { uart: data, op }),
47            op,
48        }
49    }
50
51    pub fn new_port_8250(base: usize) -> Self {
52        let data = UartData::new(base as _, IoKind::Port, |p| p as _);
53        Self::_new::<Ns16550>(data)
54    }
55
56    pub fn mmio_base_add(&mut self, offset: usize) {
57        self.data.base += offset;
58    }
59
60    pub fn new_by_fdt_node(node: &Node<'_>, f: FnPhysToVirt) -> Option<Self> {
61        let reg = node.reg()?.next()?;
62
63        let io_kind = IoKind::Mmio32;
64
65        // TODO: support io kind detect
66
67        let uart = UartData::new(reg.address, io_kind, f);
68
69        for c in node.compatibles() {
70            macro_rules! of_uart {
71                ($name:ty, $compatible:expr) => {
72                    for want in $compatible {
73                        if c.contains(want) {
74                            return Some(Uart::_new::<$name>(uart));
75                        }
76                    }
77                };
78            }
79
80            of_uart!(AuxMini, ["brcm,bcm2835-aux-uart"]);
81            of_uart!(Pl011, ["arm,pl011", "arm,primecell"]);
82            of_uart!(Ns16550, ["snps,dw-apb-uart"]);
83        }
84        None
85    }
86
87    pub fn set_irq_enable(&mut self, enable: bool) {
88        (self.op.set_irq_enable)(self.data, enable);
89    }
90
91    pub fn get_irq_enable(&mut self) -> bool {
92        (self.op.get_irq_enable)(self.data)
93    }
94
95    pub fn clean_irq_event(&mut self, event: IrqEvent) {
96        (self.op.clean_irq_event)(self.data, event);
97    }
98
99    pub fn get_irq_event(&mut self) -> IrqEvent {
100        (self.op.get_irq_event)(self.data)
101    }
102}
103
104#[derive(Debug, Clone, Copy, Default)]
105pub struct IrqEvent {
106    pub rx: bool,
107    pub tx: bool,
108}
109
110#[derive(Clone, Copy)]
111struct UartOp {
112    can_put: fn(UartData) -> bool,
113    put: fn(UartData, u8) -> Result<(), ErrorKind>,
114    can_get: fn(UartData) -> bool,
115    get: fn(UartData) -> Result<u8, ErrorKind>,
116    set_irq_enable: fn(UartData, bool),
117    get_irq_enable: fn(UartData) -> bool,
118    get_irq_event: fn(UartData) -> IrqEvent,
119    clean_irq_event: fn(UartData, IrqEvent),
120}
121
122pub struct Sender {
123    uart: UartData,
124    op: UartOp,
125}
126
127impl Sender {
128    pub fn write(&mut self, word: u8) -> Result<(), Error> {
129        if !self.can_write() {
130            return Err(Error::WouldBlock);
131        }
132        fence(Ordering::Release);
133        unsafe { self.write_uncheck(word)? };
134        Ok(())
135    }
136
137    pub fn mmio_base_add(&mut self, offset: usize) {
138        self.uart.base += offset;
139    }
140
141    pub fn can_write(&self) -> bool {
142        (self.op.can_put)(self.uart)
143    }
144
145    /// Write a byte to the UART.
146    ///
147    /// # Safety
148    ///
149    /// Need to check the UART status register before writing.
150    pub unsafe fn write_uncheck(&mut self, word: u8) -> Result<(), ErrorKind> {
151        (self.op.put)(self.uart, word)
152    }
153
154    pub fn write_str_blocking(&mut self, s: &str) -> core::fmt::Result {
155        for c in s.bytes() {
156            let _ = block!(self.write(c));
157        }
158        Ok(())
159    }
160
161    pub fn mmio(&self) -> usize {
162        self.uart.base
163    }
164}
165
166pub struct Receiver {
167    uart: UartData,
168    op: UartOp,
169}
170
171impl Receiver {
172    pub fn read(&mut self) -> Result<u8, Error> {
173        if !self.can_read() {
174            return Err(Error::WouldBlock);
175        }
176        fence(Ordering::Release);
177        let byte = unsafe { self.read_uncheck()? };
178        Ok(byte)
179    }
180
181    pub fn can_read(&self) -> bool {
182        (self.op.can_get)(self.uart)
183    }
184
185    pub fn mmio_base_add(&mut self, offset: usize) {
186        self.uart.base += offset;
187    }
188
189    /// Read a byte from the UART.
190    ///
191    /// # Safety
192    ///
193    /// Need to check the UART status register before reading.
194    pub unsafe fn read_uncheck(&mut self) -> Result<u8, ErrorKind> {
195        (self.op.get)(self.uart)
196    }
197}
198
199pub(crate) trait Console {
200    fn open(uart: UartData);
201    fn can_put(uart: UartData) -> bool;
202    fn put(uart: UartData, c: u8) -> Result<(), ErrorKind>;
203    fn can_get(uart: UartData) -> bool;
204    fn get(uart: UartData) -> Result<u8, ErrorKind>;
205    fn set_irq_enable(uart: UartData, enable: bool);
206    fn get_irq_enable(uart: UartData) -> bool;
207    fn get_irq_event(uart: UartData) -> IrqEvent;
208    fn clean_irq_event(uart: UartData, event: IrqEvent);
209
210    fn to_op() -> UartOp {
211        UartOp {
212            can_put: Self::can_put,
213            put: Self::put,
214            can_get: Self::can_get,
215            get: Self::get,
216            set_irq_enable: Self::set_irq_enable,
217            get_irq_enable: Self::get_irq_enable,
218            get_irq_event: Self::get_irq_event,
219            clean_irq_event: Self::clean_irq_event,
220        }
221    }
222}
223
224#[derive(Clone, Copy)]
225pub(crate) struct UartData {
226    pub base: usize,
227    pub io_kind: IoKind,
228}
229
230impl UartData {
231    fn new(base: u64, io_kind: IoKind, f: FnPhysToVirt) -> Self {
232        let mmio = f(base as _);
233
234        Self {
235            base: mmio as _,
236            io_kind,
237        }
238    }
239
240    pub fn reg_u8(&self, offset: usize) -> *mut u8 {
241        self.reg(offset)
242    }
243
244    pub fn reg<T: Sized>(&self, offset: usize) -> *mut T {
245        unsafe {
246            let ptr = self.base as *mut T;
247            ptr.add(offset)
248        }
249    }
250}
251
252pub fn init(fdt_addr: NonNull<u8>, fn_phys_to_virt: FnPhysToVirt) -> Option<Uart> {
253    let fdt = Fdt::from_ptr(fdt_addr).ok()?;
254
255    let chosen = fdt.chosen()?;
256
257    let mut io_kind = IoKind::Mmio32;
258    let node;
259    let mut is_8250 = false;
260
261    match chosen.stdout() {
262        Some(n) => node = n.node,
263        None => {
264            let (n, io) = fdt_bootargs_find_node(&chosen, &fdt)?;
265            node = n;
266            io_kind = io;
267            is_8250 = true;
268        }
269    };
270
271    let reg = node.reg()?.next()?;
272
273    let uart = UartData::new(reg.address, io_kind, fn_phys_to_virt);
274
275    if is_8250 {
276        return Some(Uart::_new::<Ns16550>(uart));
277    } else {
278        for c in node.compatibles() {
279            macro_rules! of_uart {
280                ($name:ty, $compatible:expr) => {
281                    for want in $compatible {
282                        if c.contains(want) {
283                            return Some(Uart::_new::<$name>(uart));
284                        }
285                    }
286                };
287            }
288
289            of_uart!(AuxMini, ["brcm,bcm2835-aux-uart"]);
290            of_uart!(Pl011, ["arm,pl011", "arm,primecell"]);
291            of_uart!(Ns16550, ["snps,dw-apb-uart"]);
292        }
293    }
294
295    None
296}
297
298#[derive(Clone, Copy)]
299pub enum IoKind {
300    Port,
301    Mmio,
302    Mmio16,
303    Mmio32,
304    Mmio32be,
305}
306
307impl IoKind {
308    pub fn width(&self) -> usize {
309        match self {
310            IoKind::Port => 1,
311            IoKind::Mmio => 4,
312            IoKind::Mmio16 => 2,
313            IoKind::Mmio32 => 4,
314            IoKind::Mmio32be => 4,
315        }
316    }
317}
318
319impl From<&str> for IoKind {
320    fn from(value: &str) -> Self {
321        match value {
322            "mmio" => IoKind::Mmio,
323            "mmio16" => IoKind::Mmio16,
324            "mmio32" => IoKind::Mmio32,
325            "mmio32be" => IoKind::Mmio32be,
326            "mmio32native" => {
327                if cfg!(target_endian = "little") {
328                    IoKind::Mmio32
329                } else {
330                    IoKind::Mmio32be
331                }
332            }
333            _ => IoKind::Port,
334        }
335    }
336}
337
338fn fdt_bootargs_find_node<'a>(chosen: &Chosen<'a>, fdt: &'a Fdt<'a>) -> Option<(Node<'a>, IoKind)> {
339    let bootargs = chosen.bootargs()?;
340
341    let earlycon = bootargs
342        .split_ascii_whitespace()
343        .find(|&arg| arg.contains("earlycon"))?;
344
345    let mut tmp = earlycon.split('=');
346    let _ = tmp.next()?;
347    let values = tmp.next()?;
348
349    let mut values = values.split(',');
350
351    let name = values.next()?;
352
353    if !name.contains("uart") {
354        return None;
355    }
356
357    let param2 = values.next()?;
358    let addr_str;
359    let io_kind = if param2.contains("0x") {
360        addr_str = param2;
361        IoKind::Mmio
362    } else {
363        addr_str = values.next()?;
364        IoKind::from(param2)
365    };
366
367    let mmio = u64::from_str_radix(addr_str.trim_start_matches("0x"), 16).ok()?;
368
369    for node in fdt.all_nodes() {
370        if let Some(regs) = node.reg() {
371            for reg in regs {
372                if reg.address.eq(&mmio) {
373                    return Some((node, io_kind));
374                }
375            }
376        }
377    }
378
379    None
380}
381
382#[cfg(test)]
383mod tests {
384    use super::*;
385
386    #[test]
387    fn test_uart_init() {
388        let fdt = include_bytes!("../../dtb/rk3568-firefly-roc-pc-se.dtb");
389        let fdt_addr = NonNull::new(fdt.as_ptr() as usize as _).unwrap();
390        let _ = init(fdt_addr, |r| r as _).unwrap();
391    }
392}