use crate::pac;
use embassy_embedded_hal::SetConfig;
use embedded_hal::spi::{Phase, Polarity};
static mut QSPI_TAKEN: bool = false;
pub fn init() {
unsafe {
pac::mss_config_clk_rst(
pac::mss_peripherals__MSS_PERIPH_QSPIXIP,
pac::MPFS_HAL_FIRST_HART as u8,
pac::PERIPH_RESET_STATE__PERIPHERAL_ON,
);
pac::MSS_QSPI_init();
}
}
pub struct Qspi {
_private: (),
}
impl crate::Peripheral for Qspi {
fn take() -> Option<Self> {
critical_section::with(|_| unsafe {
if QSPI_TAKEN {
None
} else {
QSPI_TAKEN = true;
Some(Self { _private: () })
}
})
}
unsafe fn steal() -> Self {
Self { _private: () }
}
}
impl embedded_hal::spi::ErrorType for Qspi {
type Error = SpiError;
}
#[derive(Debug)]
pub enum SpiError {}
impl embedded_hal::spi::Error for SpiError {
fn kind(&self) -> embedded_hal::spi::ErrorKind {
embedded_hal::spi::ErrorKind::Other
}
}
#[derive(Debug, Clone, Copy)]
pub struct SpiConfig {
pub frequency: SpiFrequency,
pub phase: Phase,
pub polarity: Polarity,
}
impl Default for SpiConfig {
fn default() -> Self {
Self {
frequency: SpiFrequency::F5_000_000,
phase: Phase::CaptureOnFirstTransition,
polarity: Polarity::IdleLow,
}
}
}
#[derive(Debug, Clone, Copy)]
#[repr(u8)]
pub enum SpiFrequency {
F75_000_000 = 1, F37_500_000 = 2, F25_000_000 = 3, F18_750_000 = 4, F15_000_000 = 5, F12_500_000 = 6, F10_714_285 = 7, F9_375_000 = 8, F8_333_333 = 9, F7_500_000 = 0xA, F6_818_181 = 0xB, F6_250_000 = 0xC, F5_769_230 = 0xD, F5_357_142 = 0xE, F5_000_000 = 0xF, }
impl SetConfig for Qspi {
type Config = SpiConfig;
type ConfigError = ();
fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> {
unsafe {
let sample = if config.phase == Phase::CaptureOnFirstTransition {
pac::MSS_QSPI_SAMPLE_POSAGE_SPICLK
} else {
pac::MSS_QSPI_SAMPLE_NEGAGE_SPICLK
};
let mode = if config.polarity == Polarity::IdleLow {
pac::mss_qspi_protocol_mode_t_MSS_QSPI_MODE0
} else {
pac::mss_qspi_protocol_mode_t_MSS_QSPI_MODE3
};
let value = (sample << pac::CTRL_SAMPLE)
| ((config.frequency as u32) << pac::CTRL_CLKRATE)
| (pac::mss_qspi_io_format_t_MSS_QSPI_NORMAL << pac::CTRL_QMODE12)
| (mode << pac::CTRL_CLKIDL)
| (0 << pac::CTRL_XIP)
| (0 << pac::CTRL_XIPADDR)
| pac::CTRL_EN_MASK;
log::trace!("QSPI Control Register: {:#032b}", value);
(*pac::QSPI).CONTROL = value;
}
Ok(())
}
}
impl embedded_hal::spi::SpiBus<u8> for Qspi {
fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
let buffer_aligned: bool = words.as_ptr().align_offset(4) == 0 && words.len() > 3;
log::debug!("Reading from QSPI. Buffer aligned: {:?}", buffer_aligned);
unsafe {
while ((*pac::QSPI).STATUS & pac::STTS_READY_MASK) == 0 {
core::hint::spin_loop();
}
(*pac::QSPI).INTENABLE = 0;
let total_bytes = words.len();
(*pac::QSPI).FRAMESUP = (total_bytes as u32) & pac::FRMS_UBYTES_MASK;
let mut frame_ctrl = (total_bytes as u32) & 0xFFFF;
frame_ctrl |= 0 << pac::FRMS_CBYTES; frame_ctrl |= ((*pac::QSPI).CONTROL & pac::CTRL_QMODE12_MASK) << pac::FRMS_QSPI;
frame_ctrl |= if buffer_aligned {
pac::FRMS_FWORD_MASK
} else {
pac::FRMS_FBYTE_MASK
};
(*pac::QSPI).FRAMES = frame_ctrl;
let word_count = if buffer_aligned { total_bytes / 4 } else { 0 };
if buffer_aligned {
(*pac::QSPI).CONTROL |= pac::CTRL_FLAGSX4_MASK;
let words_32 = words.as_ptr() as *mut u32;
for i in 0..word_count {
while ((*pac::QSPI).STATUS & pac::STTS_TFFULL_MASK) != 0 {
core::hint::spin_loop();
}
(*pac::QSPI).TXDATAX4 = 0xFFFFFFFF;
while ((*pac::QSPI).STATUS & pac::STTS_RFEMPTY_MASK) != 0 {
core::hint::spin_loop();
}
*words_32.add(i) = (*pac::QSPI).RXDATAX4;
}
(*pac::QSPI).CONTROL &= !pac::CTRL_FLAGSX4_MASK;
if total_bytes % 4 != 0 {
pac::sleep_ms(10);
}
}
let remaining_start = word_count * 4;
for i in remaining_start..total_bytes {
while ((*pac::QSPI).STATUS & pac::STTS_TFFULL_MASK) != 0 {
core::hint::spin_loop();
}
(*pac::QSPI).TXDATAX1 = 0xFF;
while ((*pac::QSPI).STATUS & pac::STTS_RFEMPTY_MASK) != 0 {
core::hint::spin_loop();
}
words[i] = (*pac::QSPI).RXDATAX1;
}
while ((*pac::QSPI).STATUS & pac::STTS_RDONE_MASK) == 0 {
log::warn!("Warning: read not complete");
if ((*pac::QSPI).STATUS & pac::STTS_FLAGSX4_MASK) != 0 {
(*pac::QSPI).RXDATAX4;
} else {
(*pac::QSPI).RXDATAX1;
}
}
}
log::debug!("QSPI read complete: {:x?}", words);
Ok(())
}
fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
let buffer_aligned: bool = words.as_ptr().align_offset(4) == 0 && words.len() > 3;
log::debug!(
"Writing to QSPI {:x?}. Buffer aligned: {:?}",
words,
buffer_aligned
);
unsafe {
while ((*pac::QSPI).STATUS & pac::STTS_READY_MASK) == 0 {
core::hint::spin_loop();
}
(*pac::QSPI).INTENABLE = 0;
let total_bytes = words.len();
(*pac::QSPI).FRAMESUP = (total_bytes as u32) & pac::FRMS_UBYTES_MASK;
let mut frame_ctrl = (total_bytes as u32) & 0xFFFF;
frame_ctrl |= 0 << pac::FRMS_CBYTES; frame_ctrl |= ((*pac::QSPI).CONTROL & pac::CTRL_QMODE12_MASK) << pac::FRMS_QSPI;
frame_ctrl |= pac::FRMS_FWORD_MASK; (*pac::QSPI).FRAMES = frame_ctrl;
let word_count = if buffer_aligned { total_bytes / 4 } else { 0 };
if buffer_aligned {
(*pac::QSPI).CONTROL |= pac::CTRL_FLAGSX4_MASK;
let words_32 = words.as_ptr() as *const u32;
for i in 0..word_count {
while ((*pac::QSPI).STATUS & pac::STTS_TFFULL_MASK) != 0 {
core::hint::spin_loop();
}
(*pac::QSPI).TXDATAX4 = *words_32.add(i);
}
(*pac::QSPI).CONTROL &= !pac::CTRL_FLAGSX4_MASK;
if total_bytes % 4 != 0 {
pac::sleep_ms(10);
}
}
let remaining_start = word_count * 4;
for i in remaining_start..total_bytes {
while ((*pac::QSPI).STATUS & pac::STTS_TFFULL_MASK) != 0 {
core::hint::spin_loop();
}
(*pac::QSPI).TXDATAX1 = words[i];
}
while ((*pac::QSPI).STATUS & pac::STTS_TDONE_MASK) == 0 {
core::hint::spin_loop();
}
}
log::debug!("QSPI write complete");
Ok(())
}
fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
let buffer_aligned: bool = write.as_ptr().align_offset(4) == 0
&& read.as_ptr().align_offset(4) == 0
&& (write.len() > 3 || read.len() > 3);
log::debug!(
"QSPI transfer {:x?}. Buffer aligned: {:?}",
write,
buffer_aligned
);
unsafe {
while ((*pac::QSPI).STATUS & pac::STTS_READY_MASK) == 0 {
core::hint::spin_loop();
}
(*pac::QSPI).INTENABLE = 0;
let total_bytes = read.len().max(write.len());
(*pac::QSPI).FRAMESUP = (total_bytes as u32) & pac::FRMS_UBYTES_MASK;
let mut frame_ctrl = (total_bytes as u32) & 0xFFFF;
frame_ctrl |= 0 << pac::FRMS_CBYTES; frame_ctrl |= ((*pac::QSPI).CONTROL & pac::CTRL_QMODE12_MASK) << pac::FRMS_QSPI;
frame_ctrl |= if buffer_aligned {
pac::FRMS_FWORD_MASK
} else {
pac::FRMS_FBYTE_MASK
};
(*pac::QSPI).FRAMES = frame_ctrl;
(*pac::QSPI).CONTROL |= pac::CTRL_FLAGSX4_MASK;
let write_32 = write.as_ptr() as *const u32;
let read_32 = read.as_ptr() as *mut u32;
let word_count = if buffer_aligned { total_bytes / 4 } else { 0 };
let tx_word_count = write.len() / 4;
let rx_word_count = read.len() / 4;
if buffer_aligned {
for i in 0..word_count {
while ((*pac::QSPI).STATUS & pac::STTS_TFFULL_MASK) != 0 {
core::hint::spin_loop();
}
if i < tx_word_count {
(*pac::QSPI).TXDATAX4 = *write_32.add(i);
} else {
(*pac::QSPI).TXDATAX4 = 0xFFFFFFFF;
}
while ((*pac::QSPI).STATUS & pac::STTS_RFEMPTY_MASK) != 0 {
core::hint::spin_loop();
}
if i < rx_word_count {
*read_32.add(i) = (*pac::QSPI).RXDATAX4;
} else {
(*pac::QSPI).RXDATAX1; }
}
(*pac::QSPI).CONTROL &= !pac::CTRL_FLAGSX4_MASK;
if total_bytes % 4 != 0 {
pac::sleep_ms(10);
}
}
let remaining_start = word_count * 4;
for i in remaining_start..total_bytes {
while ((*pac::QSPI).STATUS & pac::STTS_TFFULL_MASK) != 0 {
core::hint::spin_loop();
}
if i < write.len() {
(*pac::QSPI).TXDATAX1 = write[i];
} else {
(*pac::QSPI).TXDATAX1 = 0xFF;
}
while ((*pac::QSPI).STATUS & pac::STTS_RFEMPTY_MASK) != 0 {
core::hint::spin_loop();
}
if i < read.len() {
read[i] = (*pac::QSPI).RXDATAX1;
} else {
(*pac::QSPI).RXDATAX1; }
}
while ((*pac::QSPI).STATUS & pac::STTS_RDONE_MASK) == 0 {
log::warn!("Warning: read not complete");
if ((*pac::QSPI).STATUS & pac::STTS_FLAGSX4_MASK) != 0 {
(*pac::QSPI).RXDATAX4;
} else {
(*pac::QSPI).RXDATAX1;
}
}
}
log::debug!("QSPI transfer received {:x?}", read);
Ok(())
}
fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
let buffer_aligned: bool = words.as_ptr().align_offset(4) == 0 && words.len() > 3;
log::debug!(
"QSPI transfer_in_place {:x?}. Buffer aligned: {:?}",
words,
buffer_aligned
);
unsafe {
while ((*pac::QSPI).STATUS & pac::STTS_READY_MASK) == 0 {
core::hint::spin_loop();
}
(*pac::QSPI).INTENABLE = 0;
let total_bytes = words.len();
(*pac::QSPI).FRAMESUP = (total_bytes as u32) & pac::FRMS_UBYTES_MASK;
let mut frame_ctrl = (total_bytes as u32) & 0xFFFF;
frame_ctrl |= 0 << pac::FRMS_CBYTES; frame_ctrl |= ((*pac::QSPI).CONTROL & pac::CTRL_QMODE12_MASK) << pac::FRMS_QSPI;
frame_ctrl |= if buffer_aligned {
pac::FRMS_FWORD_MASK
} else {
pac::FRMS_FBYTE_MASK
};
(*pac::QSPI).FRAMES = frame_ctrl;
let word_count = if buffer_aligned { total_bytes / 4 } else { 0 };
if buffer_aligned {
(*pac::QSPI).CONTROL |= pac::CTRL_FLAGSX4_MASK;
let words_32 = words.as_ptr() as *mut u32;
for i in 0..word_count {
while ((*pac::QSPI).STATUS & pac::STTS_TFFULL_MASK) != 0 {
core::hint::spin_loop();
}
(*pac::QSPI).TXDATAX4 = *words_32.add(i);
while ((*pac::QSPI).STATUS & pac::STTS_RFEMPTY_MASK) != 0 {
core::hint::spin_loop();
}
*words_32.add(i) = (*pac::QSPI).RXDATAX4;
}
(*pac::QSPI).CONTROL &= !pac::CTRL_FLAGSX4_MASK;
if total_bytes % 4 != 0 {
pac::sleep_ms(10);
}
}
let remaining_start = word_count * 4;
for i in remaining_start..total_bytes {
while ((*pac::QSPI).STATUS & pac::STTS_TFFULL_MASK) != 0 {
core::hint::spin_loop();
}
(*pac::QSPI).TXDATAX1 = words[i];
while ((*pac::QSPI).STATUS & pac::STTS_RFEMPTY_MASK) != 0 {
core::hint::spin_loop();
}
words[i] = (*pac::QSPI).RXDATAX1;
}
while ((*pac::QSPI).STATUS & pac::STTS_RDONE_MASK) == 0 {
log::warn!("Warning: read not complete");
if ((*pac::QSPI).STATUS & pac::STTS_FLAGSX4_MASK) != 0 {
(*pac::QSPI).RXDATAX4;
} else {
(*pac::QSPI).RXDATAX1;
}
}
}
log::debug!("QSPI transfer_in_place received {:x?}", words);
Ok(())
}
fn flush(&mut self) -> Result<(), Self::Error> {
Ok(())
}
}
impl embedded_hal_async::spi::SpiBus<u8> for Qspi {
async fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
embedded_hal::spi::SpiBus::read(self, words)
}
async fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
embedded_hal::spi::SpiBus::write(self, words)
}
async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
embedded_hal::spi::SpiBus::transfer(self, read, write)
}
async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
embedded_hal::spi::SpiBus::transfer_in_place(self, words)
}
async fn flush(&mut self) -> Result<(), Self::Error> {
embedded_hal::spi::SpiBus::flush(self)
}
}