amba_pl011 0.1.0

seminix 内核库控制台 pl011 实现
Documentation
//! seminix 内核库控制台 pl011 实现
#![no_std]
#![allow(unsafe_code)]
#![allow(clippy::cast_lossless)]

use core::ops::{Deref, DerefMut};

use console_impl::ConsoleImpl;
use semx_processor::cpu_relax;
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! {
    #[doc(hidden)]
    pub 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),
    }
}

/// Pl011 串口
pub struct Pl011 {
    /// 寄存器基址
    pub regs: usize,
    /// 时钟频率
    pub clock: u32,
    /// 波特率
    pub baudrate: u32,
}

impl Pl011 {
    fn ptr(&self) -> *mut Pl011Registers {
        self.regs as *mut _
    }
}

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

        self.dr.set(*c as u32);

        while self.fr.matches_all(Flags::FR::BUSY) {
            cpu_relax();
        }
    }
}

impl ConsoleImpl for Pl011 {
    fn init(&mut self) {
        let mut temp = 16 * self.baudrate;
        let divider = self.clock / temp;
        let remainder = self.clock % temp;
        temp = (8 * remainder) / self.baudrate;
        let fraction = (temp >> 1) + (temp & 1);

        let reg = &mut **self;

        reg.cr.set(0);
        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(&mut self, s: &[u8]) {
        let reg = &mut **self;
        for c in s {
            reg.putc(c);
        }
    }
}

impl Deref for Pl011 {
    type Target = Pl011Registers;

    fn deref(&self) -> &Self::Target {
        unsafe { &*self.ptr() }
    }
}

impl DerefMut for Pl011 {
    fn deref_mut(&mut self) -> &mut Self::Target {
        unsafe { &mut *self.ptr() }
    }
}