Files
zynq7000-rs/zynq/zynq7000/src/qspi.rs
Robin Mueller 51afdd01e6
Some checks failed
ci / Check build (push) Has been cancelled
ci / Check formatting (push) Has been cancelled
ci / Check Documentation Build (push) Has been cancelled
ci / Clippy (push) Has been cancelled
ci / Check build (pull_request) Has been cancelled
ci / Check formatting (pull_request) Has been cancelled
ci / Check Documentation Build (pull_request) Has been cancelled
ci / Clippy (pull_request) Has been cancelled
smaller tweaks and docs API unification
2025-10-09 10:56:21 +02:00

282 lines
7.2 KiB
Rust

use arbitrary_int::{u2, u3};
pub use crate::{SpiClockPhase, SpiClockPolarity};
pub const QSPI_BASE_ADDR: usize = 0xE000D000;
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
pub enum InterfaceMode {
LegacySpi = 0,
FlashMemoryInterface = 1,
}
#[bitbybit::bitenum(u1, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
pub enum Endianness {
Little = 0,
Big = 1,
}
/// Baud rate divisor register values.
#[bitbybit::bitenum(u3, exhaustive = true)]
#[derive(Debug, PartialEq, Eq)]
pub enum BaudRateDivisor {
_2 = 0b000,
_4 = 0b001,
_8 = 0b010,
_16 = 0b011,
_32 = 0b100,
_64 = 0b101,
_128 = 0b110,
_256 = 0b111,
}
impl BaudRateDivisor {
/// Actual divisor value.
pub fn divisor(&self) -> usize {
match self {
BaudRateDivisor::_2 => 2,
BaudRateDivisor::_4 => 4,
BaudRateDivisor::_8 => 8,
BaudRateDivisor::_16 => 16,
BaudRateDivisor::_32 => 32,
BaudRateDivisor::_64 => 64,
BaudRateDivisor::_128 => 128,
BaudRateDivisor::_256 => 256,
}
}
}
// TODO: Use bitbybit debug support as soon as support for write fields has been implemented.
#[bitbybit::bitfield(u32, default = 0x0)]
#[derive(Debug)]
pub struct Config {
#[bit(31, rw)]
interface_mode: InterfaceMode,
#[bit(26, rw)]
edianness: Endianness,
#[bit(19, rw)]
holdb_dr: bool,
#[bit(16, w)]
manual_start_command: bool,
#[bit(15, rw)]
manual_start_enable: bool,
#[bit(14, rw)]
manual_cs: bool,
/// Directly drives the chip select line when CS is driven manually (bit 14 is set)
#[bit(10, rw)]
peripheral_chip_select: bool,
/// The only valid value is 0b11 (32 bits)
#[bits(6..=7, rw)]
fifo_width: u2,
#[bits(3..=5, rw)]
baud_rate_div: BaudRateDivisor,
#[bit(2, rw)]
clock_phase: SpiClockPhase,
#[bit(1, rw)]
clock_polarity: SpiClockPolarity,
/// Must be set to 1 before using QSPI, 0 is a reserved value.
#[bit(0, rw)]
mode_select: bool,
}
#[bitbybit::bitfield(u32, debug)]
pub struct InterruptStatus {
/// Write-to-clear bit.
#[bit(6, rw)]
tx_underflow: bool,
#[bit(5, r)]
rx_full: bool,
#[bit(4, r)]
rx_above_threshold: bool,
#[bit(3, r)]
tx_full: bool,
#[bit(2, r)]
tx_below_threshold: bool,
/// Write-to-clear bit.
#[bit(0, rw)]
rx_overrun: bool,
}
#[bitbybit::bitfield(u32)]
pub struct InterruptControl {
#[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_not_full: bool,
#[bit(0, w)]
rx_overrun: bool,
}
#[bitbybit::bitfield(u32, debug)]
pub struct InterruptMask {
#[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_not_full: bool,
#[bit(0, r)]
rx_overrun: bool,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct SpiEnable {
#[bit(0, rw)]
enable: bool,
}
/// All the delays are in SPI reference block or external clock cycles.
#[bitbybit::bitfield(u32, debug)]
pub struct Delay {
/// Length of the master mode chip select output de-asserts between words when CPHA = 0.
#[bits(24..=31, rw)]
deassert: u8,
/// Delay between one chip select being de-activated and another being activated.
#[bits(16..=23, rw)]
between: u8,
/// Length between last bit of current word and first bit of next word.
#[bits(8..=15, rw)]
after: u8,
/// Delay between setting chip select low and first bit transfer.
#[bits(0..=7, rw)]
init: u8,
}
#[bitbybit::bitfield(u32)]
pub struct Gpio {
/// Active low write-protect bit.
#[bit(0, rw)]
write_protect_n: bool,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct LoopbackMasterClockDelay {
/// Use internal loopback master clock for read data capturing when the baud rate divisor
/// is 2.
#[bit(5, rw)]
use_loopback: bool,
#[bits(3..=4,rw)]
delay_1: u2,
#[bits(0..=2 ,rw)]
delay_0: u3,
}
#[bitbybit::bitenum(u8, exhaustive = false)]
#[derive(Debug, PartialEq, Eq)]
pub enum InstructionCode {
Read = 0x03,
FastRead = 0x0B,
FastReadDualOutput = 0x3B,
FastReadQuadOutput = 0x6B,
FastReadDualIo = 0xBB,
FastReadQuadIo = 0xEB,
}
#[bitbybit::bitfield(u32, default = 0x0, debug)]
pub struct LinearQspiConfig {
#[bit(31, rw)]
enable_linear_mode: bool,
#[bit(30, rw)]
both_memories: bool,
/// Only has a meaning is bit 30 is set (both memories).
#[bit(29, rw)]
separate_memory_bus: bool,
/// Upper memory page, if set. Only has a meaning if bit 30 is set and bit 29 / bit 31 are
/// cleared.
///
/// In LQSPI mode, address bit 25 will indicate the lower (0) or upper (1) page.
/// In IO mode, this bit selects the lower or upper memory.
#[bit(28, rw)]
upper_memory_page: bool,
#[bit(25, rw)]
mode_enable: bool,
#[bit(24, rw)]
mode_on: bool,
#[bits(16..=23, rw)]
mode_bits: u8,
#[bits(8..=10, rw)]
num_dummy_bytes: u3,
#[bits(0..=7, rw)]
instruction_code: Option<InstructionCode>,
}
#[bitbybit::bitfield(u32, debug)]
pub struct LinearQspiStatus {
#[bit(2, rw)]
data_fsm_error: bool,
#[bit(1, rw)]
axi_write_command_received: bool,
}
/// QSPI register access.
#[derive(derive_mmio::Mmio)]
#[repr(C)]
pub struct Qspi {
config: Config,
interrupt_status: InterruptStatus,
#[mmio(Write)]
interrupt_enable: InterruptControl,
#[mmio(Write)]
interrupt_disable: InterruptControl,
#[mmio(PureRead)]
interupt_mask: InterruptMask,
spi_enable: SpiEnable,
delay: Delay,
/// Transmits 1-byte command and 3-byte data OR 4-byte data.
#[mmio(Write)]
tx_data_00: u32,
#[mmio(PureRead)]
rx_data: u32,
slave_idle_count: u32,
/// Defines the level at which the TX FIFO not full interrupt is generated.
tx_fifo_threshold: u32,
/// Defines the level at which the RX FIFO not empty interrupt is generated.
rx_fifo_threshold: u32,
gpio: Gpio,
_reserved0: u32,
loopback_master_clock_delay: LoopbackMasterClockDelay,
_reserved1: [u32; 0x11],
/// Transmits 1-byte command.
#[mmio(Write)]
tx_data_01: u32,
/// Transmits 1-byte command and 1-byte data.
#[mmio(Write)]
tx_data_10: u32,
/// Transmits 1-byte command and 2-byte data.
#[mmio(Write)]
tx_data_11: u32,
_reserved2: [u32; 0x5],
linear_qspi_config: LinearQspiConfig,
linear_qspi_status: LinearQspiStatus,
_reserved3: [u32; 0x15],
/// Module ID value with reset value 0x1090101.
module_id: u32,
}
static_assertions::const_assert_eq!(core::mem::size_of::<Qspi>(), 0x100);
impl Qspi {
/// Create a new QSPI MMIO instance for for QSPI controller at address [QSPI_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() -> MmioQspi<'static> {
unsafe { Self::new_mmio_at(QSPI_BASE_ADDR) }
}
}