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
110
111
112
113
114
//! Serial driver
// This module is only intended to be used internally, hence the semver
// exemption. It probably should be in a HAL crate, but there's no HAL crate
// for RZ/A1.
#![cfg(feature = "semver-exempt")]
use super::gpio::{AltMode, GpioExt};

/// The extension trait for `rza1::SCIFx` (e.g., [`rza1::SCIF0`]).
///
/// # Safety
///
/// This is only meant to be implemented on `rza1::SCIFx`.
pub unsafe trait ScifExt:
    core::ops::Deref<Target = rza1::scif0::RegisterBlock> + Sized
{
    fn global() -> Self {
        assert_eq!(core::mem::size_of::<Self>(), 0);
        unsafe { core::mem::zeroed() }
    }

    fn configure_pins(&self, gpio: &rza1::gpio::RegisterBlock);

    fn enable_clock(&self, cpg: &rza1::cpg::RegisterBlock);

    #[inline]
    fn configure_uart(&self, baud_rate: u32) {
        self.scr.write(|w| {
            w.tie()
                .clear_bit()
                .rie()
                .clear_bit()
                .te()
                .set_bit()
                .re()
                .clear_bit()
                .reie()
                .clear_bit()
                .cke()
                .internal_sck_in()
        });
        self.smr.write(|w| {
            w
                // Asynchronous
                .ca()
                .clear_bit()
                // 8-bit data
                .chr()
                .clear_bit()
                // No parity bits
                .pe()
                .clear_bit()
                // One stop bit
                .stop()
                .clear_bit()
                .cks()
                .divide_by_1()
        });
        let brr: u8 = (2083333 / baud_rate - 1)
            .try_into()
            .expect("can't satisfy the baud rate specification");
        self.brr.write(|w| w.d().bits(brr));
    }

    fn into_nb_writer(self) -> NbWriter<Self> {
        NbWriter(self)
    }
}

unsafe impl ScifExt for rza1::SCIF2 {
    #[inline]
    fn configure_pins(&self, gpio: &rza1::gpio::RegisterBlock) {
        gpio.set_alt_mode((6, 2), AltMode::Alt7);
        gpio.set_alt_mode((6, 3), AltMode::Alt7);
        gpio.set_input((6, 2));
        gpio.set_output((6, 3));
    }

    #[inline]
    fn enable_clock(&self, cpg: &rza1::cpg::RegisterBlock) {
        cpg.stbcr4.modify(|_, w| w.mstp45().clear_bit());
    }
}

/// The adapter for [`ScifExt`] that uses [`::nb`] to notify the caller of a
/// blocking situation.
pub struct NbWriter<T>(T);

// Safety: `NbWriter` can do nothing with `&Self`, and its owner can't take a
//         reference to `self.0`
unsafe impl<T> Sync for NbWriter<T> {}

impl<T: ScifExt> embedded_hal::serial::Write<u8> for NbWriter<T> {
    type Error = core::convert::Infallible;

    fn write(&mut self, word: u8) -> nb::Result<(), Self::Error> {
        let sc = &*self.0;
        if sc.fsr.read().tdfe().bit_is_set() {
            sc.ftdr.write(|w| w.d().bits(word));
            sc.fsr
                .modify(|_, w| w.tdfe().clear_bit().tend().clear_bit());
            Ok(())
        } else {
            Err(nb::Error::WouldBlock)
        }
    }

    fn flush(&mut self) -> nb::Result<(), Self::Error> {
        if self.0.fsr.read().tend().bit_is_set() {
            Ok(())
        } else {
            Err(nb::Error::WouldBlock)
        }
    }
}