2025-04-01 10:59:23 +02:00

215 lines
5.4 KiB
Rust

//! SPI register module.
use arbitrary_int::u4;
pub const SPI_0_BASE_ADDR: usize = 0xE000_6000;
pub const SPI_1_BASE_ADDR: usize = 0xE000_7000;
/// The SPI reference block will be divided by a divisor value.
#[bitbybit::bitenum(u3)]
#[derive(Debug, PartialEq, Eq)]
pub enum BaudDivSelect {
By4 = 0b001,
By8 = 0b010,
By16 = 0b011,
By32 = 0b100,
By64 = 0b101,
By128 = 0b110,
By256 = 0b111,
}
impl BaudDivSelect {
pub const fn div_value(&self) -> usize {
match self {
BaudDivSelect::By4 => 4,
BaudDivSelect::By8 => 8,
BaudDivSelect::By16 => 16,
BaudDivSelect::By32 => 32,
BaudDivSelect::By64 => 64,
BaudDivSelect::By128 => 128,
BaudDivSelect::By256 => 256,
}
}
}
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct Config {
#[bit(17, rw)]
modefail_gen_en: bool,
#[bit(16, w)]
manual_start: bool,
#[bit(15, rw)]
manual_start_enable: bool,
#[bit(14, rw)]
manual_cs: bool,
#[bits(10..=13, rw)]
cs_raw: u4,
/// Peripheral select decode, 1: Allow external 3-to-8 decode.
/// I am not sure how exactly this work, but I suspect the last three bits of the chip
/// select bits will be output directly to the 3 chip select output lines.
#[bit(9, rw)]
peri_sel: bool,
/// Uses SPI reference clock, value 1 is not supported.
#[bit(8, r)]
ref_clk: bool,
#[bits(3..=5, rw)]
baud_rate_div: Option<BaudDivSelect>,
/// Clock phase. 1: The SPI clock is inactive outside the word.
#[bit(2, rw)]
cpha: bool,
/// Clock phase. 1: The SPI clock is quiescent high.
#[bit(1, rw)]
cpol: bool,
/// Master mode enable. 1 is master mode.
#[bit(0, rw)]
master_ern: bool,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct Status {
#[bit(6, rw)]
tx_underflow: bool,
#[bit(5, rw)]
rx_full: bool,
#[bit(4, rw)]
rx_not_empty: bool,
#[bit(3, rw)]
tx_full: bool,
#[bit(2, rw)]
tx_trig: bool,
#[bit(1, rw)]
mode_fault: bool,
/// Receiver overflow interrupt.
#[bit(0, rw)]
rx_ovr: bool,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct InterruptRegWriteOnly {
#[bit(6, w)]
tx_underflow: bool,
#[bit(5, w)]
rx_full: bool,
#[bit(4, w)]
rx_not_empty: bool,
#[bit(3, w)]
tx_full: bool,
#[bit(2, w)]
tx_trig: bool,
#[bit(1, w)]
mode_fault: bool,
/// Receiver overflow interrupt.
#[bit(0, w)]
rx_ovr: bool,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct InterruptRegReadOnly {
#[bit(6, r)]
tx_underflow: bool,
#[bit(5, r)]
rx_full: bool,
#[bit(4, r)]
rx_not_empty: bool,
#[bit(3, r)]
tx_full: bool,
#[bit(2, r)]
tx_trig: bool,
#[bit(1, r)]
mode_fault: bool,
/// Receiver overflow interrupt.
#[bit(0, r)]
rx_ovr: bool,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct FifoWrite {
#[bits(0..=7, rw)]
data: u8,
}
#[bitbybit::bitfield(u32)]
#[derive(Debug)]
pub struct FifoRead {
#[bits(0..=7, r)]
data: u8,
}
/// The numbers specified in the register fields are always specified in number of
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct DelayControl {
/// Number of cycles the chip select is de-asserted between words when CPHA = 0
#[bits(24..=31, rw)]
inter_word_cs_deassert: u8,
/// Delay between one chip select being de-activated, and activation of another.
#[bits(16..=23, rw)]
between_cs_assertion: u8,
/// Delay between words.
#[bits(8..=15, rw)]
inter_word: u8,
/// Added delay between assertion of slave select and first bit transfer.
#[bits(0..=7, rw)]
cs_to_first_bit: u8,
}
/// Register block specification for both PS SPIs.
#[derive(derive_mmio::Mmio)]
#[repr(C)]
pub struct Spi {
cr: Config,
sr: Status,
/// Interrupt Enable Register.
#[mmio(Write)]
ier: InterruptRegWriteOnly,
/// Interrupt Disable Register.
#[mmio(Write)]
idr: InterruptRegWriteOnly,
/// Interrupt Mask Register.
#[mmio(PureRead)]
imr: InterruptRegReadOnly,
enable: u32,
delay_control: DelayControl,
#[mmio(Write)]
txd: FifoWrite,
#[mmio(Read)]
rxd: FifoRead,
sicr: u32,
tx_trig: u32,
rx_trig: u32,
_reserved: [u32; 0x33],
// Reset value: 0x90106
#[mmio(PureRead)]
mod_id: u32,
}
static_assertions::const_assert_eq!(core::mem::size_of::<Spi>(), 0x100);
impl Spi {
/// Create a new SPI MMIO instance for SPI0 at address [SPI_0_BASE_ADDR].
///
/// # Safety
///
/// This API can be used to potentially create a driver to the same peripheral structure
/// from multiple threads. The user must ensure that concurrent accesses are safe and do not
/// interfere with each other.
pub const unsafe fn new_mmio_fixed_0() -> MmioSpi<'static> {
unsafe { Self::new_mmio_at(SPI_0_BASE_ADDR) }
}
/// Create a new SPI MMIO instance for SPI1 at address [SPI_1_BASE_ADDR].
///
/// # Safety
///
/// This API can be used to potentially create a driver to the same peripheral structure
/// from multiple threads. The user must ensure that concurrent accesses are safe and do not
/// interfere with each other.
pub const unsafe fn new_mmio_fixed_1() -> MmioSpi<'static> {
unsafe { Self::new_mmio_at(SPI_1_BASE_ADDR) }
}
}