pl011 0.1.1

simink pl011 串口驱动实现
Documentation
#![no_std]

use processor::cpu_relax;
use simink_serial::{self, Serial, SerialInstance};
use simink_spinlock::{self, SpinLock};
use tock_registers::{
    interfaces::{Readable, Writeable},
    register_bitfields, register_structs,
    registers::{ReadOnly, ReadWrite, WriteOnly},
};

register_bitfields! {
    u32,

    Data [
        IO OFFSET(0) NUMBITS(32) []
    ],

    Flags [
        FR OFFSET(0) NUMBITS(32) [
            TXFE = 0x80,
            RXFF = 0x40,
            TXFF = 0x20,
            RXFE = 0x10,
            BUSY = 0x08,
        ]
    ],

    Baud [
        BAUD OFFSET(0) NUMBITS(32) []
    ],

    LineControl [
        BRK OFFSET(0) NUMBITS(1) [],
        PEN OFFSET(1) NUMBITS(1) [],
        EPS OFFSET(2) NUMBITS(1) [],
        STP2 OFFSET(3) NUMBITS(1) [],
        FEN OFFSET(4) NUMBITS(1) [],

        WLEN OFFSET(5) NUMBITS(2) [
            WLEN_5 = 0,
            WLEN_6 = 1,
            WLEN_7 = 2,
            WLEN_8 = 3,
        ],

        SPS OFFSET(7) NUMBITS(1) [],
    ],

    Control [
        UARTEN OFFSET(0) NUMBITS(1) [],
        SIREN OFFSET(1) NUMBITS(1) [],
        IIRLP OFFSET(2) NUMBITS(1) [],
        LPE OFFSET(7) NUMBITS(1) [],
        TXE OFFSET(8) NUMBITS(1) [],
        RXE OFFSET(9) NUMBITS(1) [],
        DTR OFFSET(10) NUMBITS(1) [],
        RTS OFFSET(11) NUMBITS(1) [],
        OUT1 OFFSET(12) NUMBITS(1) [],
        OUT2 OFFSET(13) NUMBITS(1) [],
        RTSEN OFFSET(14) NUMBITS(1) [],
        CTSEN OFFSET(15) NUMBITS(1) [],
    ]
}

register_structs! {
    Pl011Registers {
        // Data register
        (0x00 => dr: WriteOnly<u32, Data::Register>),
        // ecr - 0x04 Error clear register (Write)
        // pl010_lcrh - 0x08 Line control register, high byte
        // pl010_lcrm - 0x0C Line control register, middle byte
        // pl010_lcrl - 0x10 Line control register, low byte
        // pl010_cr - 0x14 Control register
        (0x04 => _reserved),
        // Flag register (Read only)
        (0x18 => fr: ReadOnly<u32, Flags::Register>),
        // pl011_rlcr - 0x1c Receive line control register
        // lpr - 0x20 IrDA low-power counter register
        (0x1c => _reserved2),
        // Integer baud rate register
        (0x24 => ibrd: ReadWrite<u32, Baud::Register>),
        // Fractional baud rate register
        (0x28 => fbrd: ReadWrite<u32, Baud::Register>),
        // Line control register
        (0x2c => lcrh: ReadWrite<u32, LineControl::Register>),
        // Control register
        (0x30 => cr: ReadWrite<u32, Control::Register>),
        (0x34 => @END),
    }
}

pub struct Pl011State {
    val: SpinLock<*mut Pl011Registers>,
}

impl Default for Pl011State {
    fn default() -> Self {
        Self::new()
    }
}

impl Pl011State {
    pub fn new() -> Self {
        Self::default()
    }

    #[inline]
    fn putc(reg: &Pl011Registers, c: u8) {
        while reg.fr.matches_any(Flags::FR::TXFF) {
            cpu_relax();
        }

        reg.dr.set(c as u32);

        while reg.fr.matches_any(Flags::FR::BUSY) {
            cpu_relax();
        }
    }
}

impl<S> SerialInstance<S> for Pl011State {
    fn init(&self, serial: &Serial<S>) {
        let reg;
        unsafe {
            let mut val = self.val.lock();
            *val = serial.virtbase as *mut Pl011Registers;
            reg = val.as_ref().unwrap();
        }
        reg.cr.set(0);

        let mut temp = 16 * serial.baud;
        let divider = serial.clk / temp;
        let remainder = serial.clk % temp;
        temp = (8 * remainder) / serial.baud;
        let fraction = (temp >> 1) + (temp & 1);

        reg.ibrd.set(divider);
        reg.fbrd.set(fraction);

        reg.lcrh
            .write(LineControl::WLEN::WLEN_8 + LineControl::FEN::SET);

        reg.cr.write(
            Control::UARTEN::SET + Control::TXE::SET + Control::RXE::SET + Control::RTS::SET,
        );
    }

    fn write(&self, s: &str) {
        let reg;
        unsafe {
            reg = self.val.lock().as_ref().unwrap();
        }
        for c in s.bytes() {
            if c == b'\n' {
                Pl011State::putc(reg, b'\r');
            }
            Pl011State::putc(reg, c);
        }
    }
}