215 lines
5.4 KiB
Rust
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) }
|
|
}
|
|
}
|