use ruspiro_gpio::GPIO;
use ruspiro_mmio_register::define_mmio_register;
use ruspiro_register::RegisterFieldValue;
use ruspiro_timer as timer;
use crate::UartResult;
#[cfg(feature = "ruspiro_pi3")]
const PERIPHERAL_BASE: usize = 0x3F00_0000;
const UART0_BASE: usize = PERIPHERAL_BASE + 0x0020_1000;
pub(crate) fn init(clock_rate: u32, baud_rate: u32) -> UartResult<()> {
GPIO.with_mut(|gpio| {
let _ = gpio.get_pin(32).map(|pin| pin.into_alt_f3());
let _ = gpio.get_pin(33).map(|pin| pin.into_alt_f3());
Ok(())
})
.map(|_| {
let baud16: u32 = baud_rate * 16;
let int_div: u32 = clock_rate / baud16;
let frac_div2 = (clock_rate % baud16) * 8 / baud_rate;
let frac_div = (frac_div2 / 2) + (frac_div2 % 2);
UART0_CR::Register.set(0);
UART0_IMSC::Register.set(0x0);
UART0_ICR::Register.set(0x7FF);
UART0_IBRD::Register.set(int_div);
UART0_FBRD::Register.set(frac_div);
UART0_IFLS::Register.write(UART0_IFLS::RXIFSEL, Ifsel::Filled_1_8 as u32);
UART0_LCRH::Register.write_value(
RegisterFieldValue::<u32>::new(UART0_LCRH::WLEN, Wlen::DataLen8 as u32)
| RegisterFieldValue::<u32>::new(UART0_LCRH::FEN, 0x1),
);
UART0_CR::Register.write_value(
RegisterFieldValue::<u32>::new(UART0_CR::UART_EN, 0x1)
| RegisterFieldValue::<u32>::new(UART0_CR::TXE, 0x1)
| RegisterFieldValue::<u32>::new(UART0_CR::RXE, 0x1),
);
UART0_IMSC::Register.write_value(
RegisterFieldValue::<u32>::new(UART0_IMSC::INT_RX, 0x1)
| RegisterFieldValue::<u32>::new(UART0_IMSC::INT_RT, 0x1)
| RegisterFieldValue::<u32>::new(UART0_IMSC::INT_OE, 0x1),
);
})
}
pub(crate) fn release() {
GPIO.with_mut(|gpio| {
gpio.free_pin(32);
gpio.free_pin(33);
});
}
pub(crate) fn write_byte(data: u8) {
while UART0_FR::Register.read(UART0_FR::TXFF) == 1 {
timer::sleepcycles(10);
}
UART0_DR::Register.set(data as u32);
}
pub(crate) fn read_byte() -> Option<u8> {
while UART0_FR::Register.read(UART0_FR::RXFE) == 1 {
timer::sleepcycles(10);
}
Some((UART0_DR::Register.get() & 0xFF) as u8)
}
#[allow(dead_code, non_camel_case_types, clippy::enum_variant_names)]
enum Ifsel {
Filled_1_8 = 0,
Filled_1_4 = 1,
Filled_1_2 = 2,
Filled_3_4 = 3,
Filled_7_8 = 4,
}
#[allow(dead_code)]
enum Wlen {
DataLen8 = 3,
DataLen7 = 2,
DataLen6 = 1,
DataLen5 = 0,
}
define_mmio_register![
UART0_DR<ReadWrite<u32>@(UART0_BASE + 0x00)>,
UART0_RSRECR<ReadWrite<u32>@(UART0_BASE + 0x04)>,
UART0_FR<ReadWrite<u32>@(UART0_BASE + 0x18)> {
TXFE OFFSET(7),
RXFF OFFSET(6),
TXFF OFFSET(5),
RXFE OFFSET(4),
BUSY OFFSET(3)
},
UART0_IBRD<ReadWrite<u32>@(UART0_BASE + 0x24)>,
UART0_FBRD<ReadWrite<u32>@(UART0_BASE + 0x28)>,
UART0_LCRH<ReadWrite<u32>@(UART0_BASE + 0x2C)> {
SPS OFFSET(7),
WLEN OFFSET(5) BITS(2),
FEN OFFSET(4),
STP2 OFFSET(3),
EPS OFFSET(2),
PEN OFFSET(1),
BRK OFFSET(0)
},
UART0_CR<ReadWrite<u32>@(UART0_BASE + 0x30)> {
CTSEN OFFSET(15),
RTSEN OFFSET(14),
OUT2 OFFSET(13),
OUT1 OFFSET(12),
RTS OFFSET(11),
DTR OFFSET(10),
RXE OFFSET(9),
TXE OFFSET(8),
LBE OFFSET(7),
UART_EN OFFSET(0)
},
UART0_IFLS<ReadWrite<u32>@(UART0_BASE + 0x34)> {
RXIFSEL OFFSET(3) BITS(3),
TXIFSEL OFFSET(0) BITS(3)
},
UART0_IMSC<ReadWrite<u32>@(UART0_BASE + 0x38)> {
INT_OE OFFSET(10), INT_BE OFFSET(9),
INT_PE OFFSET(8),
INT_FE OFFSET(7),
INT_RT OFFSET(6), INT_TX OFFSET(5), INT_RX OFFSET(4), INT_DSRM OFFSET(3),
INT_DCDM OFFSET(2),
INT_CTSM OFFSET(1)
},
UART0_RIS<ReadWrite<u32>@(UART0_BASE + 0x3C)>,
UART0_MIS<ReadWrite<u32>@(UART0_BASE + 0x40)>,
UART0_ICR<ReadWrite<u32>@(UART0_BASE + 0x44)>
];