Skip to main content

mpfs_hal/
spi.rs

1use crate::{Mutex, pac};
2use embassy_embedded_hal::SetConfig;
3use embedded_hal::spi::{ErrorType, Operation, Phase, Polarity};
4use paste::paste;
5
6// Master mode only
7// TODO: Add slave mode
8
9pub fn init() {
10    unsafe {
11        pac::mss_config_clk_rst(
12            pac::mss_peripherals__MSS_PERIPH_SPI0,
13            pac::MPFS_HAL_FIRST_HART as u8,
14            pac::PERIPH_RESET_STATE__PERIPHERAL_ON,
15        );
16        pac::mss_config_clk_rst(
17            pac::mss_peripherals__MSS_PERIPH_SPI1,
18            pac::MPFS_HAL_FIRST_HART as u8,
19            pac::PERIPH_RESET_STATE__PERIPHERAL_ON,
20        );
21        pac::MSS_SPI_init(&raw mut pac::g_mss_spi0_lo);
22        pac::MSS_SPI_init(&raw mut pac::g_mss_spi1_lo);
23
24        // Add for slave mode
25        // pac::PLIC_SetPriority(pac::PLIC_IRQn_Type_PLIC_SPI0_INT_OFFSET, 2);
26        // pac::PLIC_SetPriority(pac::PLIC_IRQn_Type_PLIC_SPI1_INT_OFFSET, 2);
27        // pac::PLIC_EnableIRQ(pac::PLIC_IRQn_Type_PLIC_SPI0_INT_OFFSET);
28        // pac::PLIC_EnableIRQ(pac::PLIC_IRQn_Type_PLIC_SPI1_INT_OFFSET);
29    }
30    debug!("SPI initialized");
31}
32
33pub trait SpiPeripheral: crate::Peripheral {
34    #[doc(hidden)]
35    fn address(&self) -> *mut pac::mss_spi_instance_t;
36    #[doc(hidden)]
37    fn number(&self) -> u8;
38    #[doc(hidden)]
39    fn slave_num(&self) -> u8;
40}
41
42#[derive(Debug)]
43pub enum SpiError {
44    InvalidClockDiv,
45}
46
47impl embedded_hal::spi::Error for SpiError {
48    fn kind(&self) -> embedded_hal::spi::ErrorKind {
49        embedded_hal::spi::ErrorKind::Other
50    }
51}
52
53static SPI0_IN_USE: Mutex = Mutex::new();
54static SPI1_IN_USE: Mutex = Mutex::new();
55
56macro_rules! impl_spi {
57    ($n:expr, $slave_num:expr) => {
58        paste! {
59            impl_spi!([<Spi $n Slave $slave_num>], [<SPI $n _SLAVE $slave_num _TAKEN>], $n, $slave_num, [<g_mss_spi $n _lo>]);
60        }
61    };
62
63    // E.g. impl_uart!(SPI0_SLAVE0, SPI0_SLAVE0_TAKEN, 0, 0, g_mss_spi_0_lo);
64    ($PERIPH:ident, $PERIPH_TAKEN:ident, $spi_num:expr, $slave_num:expr, $instance:ident) => {
65        pub struct $PERIPH {
66            _private: (),
67        }
68        static mut $PERIPH_TAKEN: bool = false;
69
70        impl crate::Peripheral for $PERIPH {
71            fn take() -> Option<Self> {
72                critical_section::with(|_| unsafe {
73                    if $PERIPH_TAKEN {
74                        None
75                    } else {
76                        $PERIPH_TAKEN = true;
77                        Some(Self { _private: () })
78                    }
79                })
80            }
81
82            unsafe fn steal() -> Self {
83                Self { _private: () }
84            }
85        }
86        impl SpiPeripheral for $PERIPH {
87            fn address(&self) -> *mut pac::mss_spi_instance_t {
88                &raw mut pac::$instance
89            }
90
91            fn number(&self) -> u8 {
92                $spi_num
93            }
94
95            fn slave_num(&self) -> u8 {
96                $slave_num
97            }
98        }
99
100    };
101}
102
103impl_spi!(0, 0);
104impl_spi!(0, 1);
105impl_spi!(1, 0);
106impl_spi!(1, 1);
107
108#[derive(Debug, Clone, Copy)]
109pub struct SpiConfig {
110    /// Clock divider, must be even between 2 to 512.
111    /// The base clock is 150MHz, so the actual clock will be 150MHz / clock_div.
112    /// Any value less than 8 results in instability on the CS line.
113    clock_div: u16,
114    phase: Phase,
115    polarity: Polarity,
116}
117
118impl Default for SpiConfig {
119    fn default() -> Self {
120        Self {
121            clock_div: 8,
122            phase: Phase::CaptureOnFirstTransition,
123            polarity: Polarity::IdleLow,
124        }
125    }
126}
127
128impl SpiConfig {
129    pub fn new(clock_div: u16, phase: Phase, polarity: Polarity) -> Result<Self, SpiError> {
130        if clock_div < 2 || clock_div > 512 || clock_div % 2 != 0 {
131            return Err(SpiError::InvalidClockDiv);
132        }
133        Ok(Self {
134            clock_div,
135            phase,
136            polarity,
137        })
138    }
139}
140
141pub struct Spi<T: SpiPeripheral> {
142    peripheral: T,
143}
144
145impl<T: SpiPeripheral> ErrorType for Spi<T> {
146    type Error = SpiError;
147}
148
149impl<T: SpiPeripheral> Spi<T> {
150    pub fn new(peripheral: T, config: SpiConfig) -> Self {
151        let mut spi = Spi { peripheral };
152        spi.set_config(&config).unwrap();
153        spi
154    }
155}
156
157impl<T: SpiPeripheral> SetConfig for Spi<T> {
158    type Config = SpiConfig;
159    type ConfigError = SpiError;
160    fn set_config(&mut self, config: &Self::Config) -> Result<(), Self::ConfigError> {
161        let mode = match (config.phase, config.polarity) {
162            (Phase::CaptureOnFirstTransition, Polarity::IdleLow) => {
163                pac::__mss_spi_protocol_mode_t_MSS_SPI_MODE0
164            }
165            (Phase::CaptureOnSecondTransition, Polarity::IdleLow) => {
166                pac::__mss_spi_protocol_mode_t_MSS_SPI_MODE1
167            }
168            (Phase::CaptureOnFirstTransition, Polarity::IdleHigh) => {
169                pac::__mss_spi_protocol_mode_t_MSS_SPI_MODE2
170            }
171            (Phase::CaptureOnSecondTransition, Polarity::IdleHigh) => {
172                pac::__mss_spi_protocol_mode_t_MSS_SPI_MODE3
173            }
174        };
175        unsafe {
176            pac::MSS_SPI_configure_master_mode(
177                self.peripheral.address(),
178                self.peripheral.slave_num() as u32,
179                mode,
180                config.clock_div as u32,
181                pac::MSS_SPI_BLOCK_TRANSFER_FRAME_SIZE as u8, // This is ignored
182                Some(mss_spi_overflow_handler),
183            );
184        }
185        Ok(())
186    }
187}
188
189extern "C" fn mss_spi_overflow_handler(mss_spi_core: u8) {
190    unsafe {
191        if mss_spi_core != 0 {
192            // reset SPI1
193            pac::mss_config_clk_rst(
194                pac::mss_peripherals__MSS_PERIPH_SPI1,
195                pac::MPFS_HAL_FIRST_HART as u8,
196                pac::PERIPH_RESET_STATE__PERIPHERAL_OFF,
197            );
198            // Take SPI1 out of reset
199            pac::mss_config_clk_rst(
200                pac::mss_peripherals__MSS_PERIPH_SPI1,
201                pac::MPFS_HAL_FIRST_HART as u8,
202                pac::PERIPH_RESET_STATE__PERIPHERAL_ON,
203            );
204        } else {
205            // reset SPI0
206            pac::mss_config_clk_rst(
207                pac::mss_peripherals__MSS_PERIPH_SPI0,
208                pac::MPFS_HAL_FIRST_HART as u8,
209                pac::PERIPH_RESET_STATE__PERIPHERAL_OFF,
210            );
211            // Take SPI0 out of reset
212            pac::mss_config_clk_rst(
213                pac::mss_peripherals__MSS_PERIPH_SPI0,
214                pac::MPFS_HAL_FIRST_HART as u8,
215                pac::PERIPH_RESET_STATE__PERIPHERAL_ON,
216            );
217        }
218    }
219}
220
221impl<T: SpiPeripheral> embedded_hal::spi::SpiDevice for Spi<T> {
222    fn transaction(&mut self, operations: &mut [Operation<'_, u8>]) -> Result<(), Self::Error> {
223        let mutex = if self.peripheral.number() == 0 {
224            &SPI0_IN_USE
225        } else {
226            &SPI1_IN_USE
227        };
228        let lock = mutex.lock();
229
230        unsafe {
231            pac::MSS_SPI_set_slave_select(
232                self.peripheral.address(),
233                self.peripheral.slave_num() as u32,
234            );
235            for operation in operations {
236                match operation {
237                    Operation::Write(data) => {
238                        self.write(data)?;
239                    }
240                    Operation::Read(data) => {
241                        self.read(data)?;
242                    }
243                    Operation::Transfer(rx, tx) => {
244                        self.transfer(rx, tx)?;
245                    }
246                    Operation::TransferInPlace(data) => {
247                        self.transfer_in_place(data)?;
248                    }
249                    Operation::DelayNs(ns) => {
250                        pac::sleep_ms(*ns as u64 / 1000);
251                    }
252                }
253            }
254            pac::MSS_SPI_clear_slave_select(
255                self.peripheral.address(),
256                self.peripheral.slave_num() as u32,
257            );
258        }
259        mutex.release(lock);
260
261        Ok(())
262    }
263}
264
265impl<T: SpiPeripheral> Spi<T> {
266    fn write(&mut self, data: &[u8]) -> Result<(), SpiError> {
267        if data.is_empty() {
268            return Ok(());
269        }
270        let buffer_aligned: bool = data.as_ptr().align_offset(4) == 0 && data.len() > 3;
271        trace!(
272            "Writing to SPI {:x?}. Buffer aligned: {}; Byte count: {}",
273            data,
274            buffer_aligned,
275            data.len()
276        );
277
278        unsafe {
279            let spi = &mut (*(*self.peripheral.address()).hw_reg);
280
281            let word_count = if buffer_aligned { data.len() / 4 } else { 0 } as u32;
282            if word_count > 0 {
283                // Transfer 32-bit aligned words
284                let words = data.as_ptr() as *const u32;
285
286                // Setup registers - single volatile write for initial setup
287                spi.FRAMESIZE = 32;
288                spi.FRAMESUP = word_count as u32 & pac::spi::BYTESUPPER_MASK;
289                let control = (spi.CONTROL & !pac::spi::TXRXDFCOUNT_MASK)
290                    | ((word_count << pac::spi::TXRXDFCOUNT_SHIFT) & pac::spi::TXRXDFCOUNT_MASK);
291                spi.CONTROL = control | pac::spi::CTRL_ENABLE_MASK;
292                core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst);
293
294                // Flush the receive FIFO - needs volatile read
295                while (core::ptr::read_volatile(&spi.STATUS) & pac::spi::RX_FIFO_EMPTY_MASK) == 0 {
296                    let _ = core::ptr::read_volatile(&spi.RX_DATA);
297                }
298
299                for i in 0..(word_count as usize) {
300                    // Wait until transmit FIFO is not full - needs volatile read
301                    while (core::ptr::read_volatile(&spi.STATUS) & pac::spi::TX_FIFO_FULL_MASK) != 0
302                    {
303                        core::hint::spin_loop();
304                    }
305                    // TX_DATA write needs to be volatile
306                    core::ptr::write_volatile(&mut spi.TX_DATA, (*words.add(i)).to_be());
307                }
308
309                // Wait until the transfer is done - needs volatile read
310                while (core::ptr::read_volatile(&spi.STATUS) & pac::spi::ACTIVE_MASK) != 0 {
311                    core::hint::spin_loop();
312                }
313
314                // Reset state - single write is fine
315                spi.COMMAND |= pac::spi::TX_FIFO_RESET_MASK | pac::spi::RX_FIFO_RESET_MASK;
316                spi.CONTROL &= !pac::spi::CTRL_ENABLE_MASK;
317                core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst);
318            }
319
320            let data = &data[word_count as usize * 4..];
321
322            if !data.is_empty() {
323                // Setup registers - single volatile write for initial setup
324                spi.FRAMESIZE = 8;
325                spi.FRAMESUP = data.len() as u32 & pac::spi::BYTESUPPER_MASK;
326                let control = (spi.CONTROL & !pac::spi::TXRXDFCOUNT_MASK)
327                    | (((data.len() as u32) << pac::spi::TXRXDFCOUNT_SHIFT)
328                        & pac::spi::TXRXDFCOUNT_MASK);
329                spi.CONTROL = control | pac::spi::CTRL_ENABLE_MASK;
330                core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst);
331
332                // Flush the receive FIFO - needs volatile read
333                while (core::ptr::read_volatile(&spi.STATUS) & pac::spi::RX_FIFO_EMPTY_MASK) == 0 {
334                    let _ = core::ptr::read_volatile(&spi.RX_DATA);
335                }
336
337                for i in 0..data.len() {
338                    // Wait until transmit FIFO is not full - needs volatile read
339                    while (core::ptr::read_volatile(&spi.STATUS) & pac::spi::TX_FIFO_FULL_MASK) != 0
340                    {
341                        core::hint::spin_loop();
342                    }
343                    // TX_DATA write needs to be volatile
344                    core::ptr::write_volatile(&mut spi.TX_DATA, data[i] as u32);
345                }
346
347                // Wait until the transfer is done - needs volatile read
348                while (core::ptr::read_volatile(&spi.STATUS) & pac::spi::ACTIVE_MASK) != 0 {
349                    core::hint::spin_loop();
350                }
351
352                // Reset state - single write is fine
353                spi.COMMAND |= pac::spi::TX_FIFO_RESET_MASK | pac::spi::RX_FIFO_RESET_MASK;
354                spi.CONTROL &= !pac::spi::CTRL_ENABLE_MASK;
355                core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst);
356            }
357        }
358        Ok(())
359    }
360
361    fn read(&mut self, data: &mut [u8]) -> Result<(), SpiError> {
362        if data.is_empty() {
363            return Ok(());
364        }
365        let buffer_aligned: bool = data.as_ptr().align_offset(4) == 0 && data.len() > 3;
366
367        trace!(
368            "Reading from SPI. Buffer aligned: {}; Byte count: {}",
369            buffer_aligned,
370            data.len()
371        );
372
373        unsafe {
374            let spi = &mut (*(*self.peripheral.address()).hw_reg);
375            let word_count = if buffer_aligned { data.len() / 4 } else { 0 } as u32;
376            if word_count > 0 {
377                let words = data.as_ptr() as *mut u32;
378
379                // Setup registers - single volatile write for initial setup
380                spi.FRAMESIZE = 32;
381                spi.FRAMESUP = word_count as u32 & pac::spi::BYTESUPPER_MASK;
382                let control = (spi.CONTROL & !pac::spi::TXRXDFCOUNT_MASK)
383                    | ((word_count << pac::spi::TXRXDFCOUNT_SHIFT) & pac::spi::TXRXDFCOUNT_MASK);
384                spi.CONTROL = control | pac::spi::CTRL_ENABLE_MASK;
385                core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst);
386
387                // Flush the receive FIFO - needs volatile read
388                while (core::ptr::read_volatile(&spi.STATUS) & pac::spi::RX_FIFO_EMPTY_MASK) == 0 {
389                    let _ = core::ptr::read_volatile(&spi.RX_DATA);
390                }
391
392                for i in 0..(word_count as usize) {
393                    // Wait until transmit FIFO is not full - needs volatile read
394                    while (core::ptr::read_volatile(&spi.STATUS) & pac::spi::TX_FIFO_FULL_MASK) != 0
395                    {
396                        core::hint::spin_loop();
397                    }
398                    // TX_DATA write needs to be volatile
399                    core::ptr::write_volatile(&mut spi.TX_DATA, 0xFFFFFFFF);
400                    // Status and RX_DATA reads need to be volatile
401                    while (core::ptr::read_volatile(&spi.STATUS) & pac::spi::RX_FIFO_EMPTY_MASK)
402                        != 0
403                    {
404                        core::hint::spin_loop();
405                    }
406                    *words.add(i) = core::ptr::read_volatile(&spi.RX_DATA).to_be();
407                }
408
409                // Wait until the transfer is done - needs volatile read
410                while (core::ptr::read_volatile(&spi.STATUS) & pac::spi::ACTIVE_MASK) != 0 {
411                    core::hint::spin_loop();
412                }
413
414                // Reset state - single write is fine
415                spi.COMMAND |= pac::spi::TX_FIFO_RESET_MASK | pac::spi::RX_FIFO_RESET_MASK;
416                spi.CONTROL &= !pac::spi::CTRL_ENABLE_MASK;
417                core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst);
418            }
419
420            let data = &mut data[word_count as usize * 4..];
421
422            if !data.is_empty() {
423                // Setup registers - single volatile write for initial setup
424                spi.FRAMESIZE = 8;
425                spi.FRAMESUP = data.len() as u32 & pac::spi::BYTESUPPER_MASK;
426                let control = (spi.CONTROL & !pac::spi::TXRXDFCOUNT_MASK)
427                    | (((data.len() as u32) << pac::spi::TXRXDFCOUNT_SHIFT)
428                        & pac::spi::TXRXDFCOUNT_MASK);
429                spi.CONTROL = control | pac::spi::CTRL_ENABLE_MASK;
430                core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst);
431
432                // Flush the receive FIFO - needs volatile read
433                while (core::ptr::read_volatile(&spi.STATUS) & pac::spi::RX_FIFO_EMPTY_MASK) == 0 {
434                    let _ = core::ptr::read_volatile(&spi.RX_DATA);
435                }
436
437                for i in 0..data.len() {
438                    // Wait until transmit FIFO is not full - needs volatile read
439                    while (core::ptr::read_volatile(&spi.STATUS) & pac::spi::TX_FIFO_FULL_MASK) != 0
440                    {
441                        core::hint::spin_loop();
442                    }
443                    // TX_DATA write needs to be volatile
444                    core::ptr::write_volatile(&mut spi.TX_DATA, 0xFFFFFFFF);
445                    // Status and RX_DATA reads need to be volatile
446                    while (core::ptr::read_volatile(&spi.STATUS) & pac::spi::RX_FIFO_EMPTY_MASK)
447                        != 0
448                    {
449                        core::hint::spin_loop();
450                    }
451                    data[i] = core::ptr::read_volatile(&spi.RX_DATA) as u8;
452                }
453
454                // Wait until the transfer is done - needs volatile read
455                while (core::ptr::read_volatile(&spi.STATUS) & pac::spi::ACTIVE_MASK) != 0 {
456                    core::hint::spin_loop();
457                }
458
459                // Reset state - single write is fine
460                spi.COMMAND |= pac::spi::TX_FIFO_RESET_MASK | pac::spi::RX_FIFO_RESET_MASK;
461                spi.CONTROL &= !pac::spi::CTRL_ENABLE_MASK;
462                core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst);
463            }
464        }
465
466        Ok(())
467    }
468
469    fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), SpiError> {
470        if read.is_empty() && write.is_empty() {
471            return Ok(());
472        }
473        let buffer_aligned: bool = write.as_ptr().align_offset(4) == 0
474            && read.as_ptr().align_offset(4) == 0
475            && (write.len() > 3 || read.len() > 3);
476
477        trace!(
478            "Transferring from SPI. Read len: {}; Write: {:x?}; Buffer aligned: {}",
479            read.len(),
480            write,
481            buffer_aligned
482        );
483
484        let total_bytes = read.len().max(write.len());
485        let mut tx_bytes_sent = 0;
486        let mut rx_bytes_received = 0;
487
488        unsafe {
489            let spi = &mut (*(*self.peripheral.address()).hw_reg);
490            let word_count = if buffer_aligned { total_bytes / 4 } else { 0 } as u32;
491            if word_count > 0 {
492                let tx_words = write.as_ptr() as *mut u32;
493                let rx_words = read.as_ptr() as *mut u32;
494
495                // Setup registers - single volatile write for initial setup
496                spi.FRAMESIZE = 32;
497                spi.FRAMESUP = word_count as u32 & pac::spi::BYTESUPPER_MASK;
498                let control = (spi.CONTROL & !pac::spi::TXRXDFCOUNT_MASK)
499                    | ((word_count << pac::spi::TXRXDFCOUNT_SHIFT) & pac::spi::TXRXDFCOUNT_MASK);
500                spi.CONTROL = control | pac::spi::CTRL_ENABLE_MASK;
501                core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst);
502
503                // Flush the receive FIFO - needs volatile read
504                while (core::ptr::read_volatile(&spi.STATUS) & pac::spi::RX_FIFO_EMPTY_MASK) == 0 {
505                    let _ = core::ptr::read_volatile(&spi.RX_DATA);
506                }
507
508                for i in 0..(word_count as usize) {
509                    // Wait until transmit FIFO is not full - needs volatile read
510                    while (core::ptr::read_volatile(&spi.STATUS) & pac::spi::TX_FIFO_FULL_MASK) != 0
511                    {
512                        core::hint::spin_loop();
513                    }
514                    if tx_bytes_sent + 4 <= write.len() {
515                        // TX_DATA write needs to be volatile
516                        core::ptr::write_volatile(&mut spi.TX_DATA, (*tx_words.add(i)).to_be());
517                        tx_bytes_sent += 4;
518                    } else if tx_bytes_sent < write.len() {
519                        let mut tx_data = 0x0;
520                        let tx_bytes_remaining = write.len() - tx_bytes_sent;
521                        while tx_bytes_sent < write.len() {
522                            tx_data <<= 8;
523                            tx_data |= write[tx_bytes_sent] as u32;
524                            tx_bytes_sent += 1;
525                        }
526                        for _ in 0..(4 - tx_bytes_remaining) {
527                            tx_data <<= 8;
528                            tx_data |= 0xFF;
529                        }
530                        // TX_DATA write needs to be volatile
531                        core::ptr::write_volatile(&mut spi.TX_DATA, tx_data);
532                    } else {
533                        // TX_DATA write needs to be volatile
534                        core::ptr::write_volatile(&mut spi.TX_DATA, 0xFFFFFFFF);
535                    }
536                    // Status and RX_DATA reads need to be volatile
537                    while (core::ptr::read_volatile(&spi.STATUS) & pac::spi::RX_FIFO_EMPTY_MASK)
538                        != 0
539                    {
540                        core::hint::spin_loop();
541                    }
542                    let word = core::ptr::read_volatile(&spi.RX_DATA);
543                    if rx_bytes_received + 4 <= read.len() {
544                        *rx_words.add(i) = word.to_be();
545                        rx_bytes_received += 4;
546                    } else if rx_bytes_received < read.len() {
547                        let rx_bytes_remaining = read.len() - rx_bytes_received;
548                        for i in 0..(4 - rx_bytes_remaining) {
549                            read[rx_bytes_received] = (word >> (8 * i)) as u8;
550                            rx_bytes_received += 1;
551                        }
552                    }
553                }
554
555                // Wait until the transfer is done - needs volatile read
556                while (core::ptr::read_volatile(&spi.STATUS) & pac::spi::ACTIVE_MASK) != 0 {
557                    core::hint::spin_loop();
558                }
559
560                // Reset state - single write is fine
561                spi.COMMAND |= pac::spi::TX_FIFO_RESET_MASK | pac::spi::RX_FIFO_RESET_MASK;
562                spi.CONTROL &= !pac::spi::CTRL_ENABLE_MASK;
563                core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst);
564            }
565
566            let write = &write[tx_bytes_sent..];
567            let read = &mut read[rx_bytes_received..];
568            let remaining_bytes = read.len().max(write.len());
569
570            if remaining_bytes > 0 {
571                // Setup registers - single volatile write for initial setup
572                spi.FRAMESIZE = 8;
573                spi.FRAMESUP = remaining_bytes as u32 & pac::spi::BYTESUPPER_MASK;
574                let control = (spi.CONTROL & !pac::spi::TXRXDFCOUNT_MASK)
575                    | (((remaining_bytes as u32) << pac::spi::TXRXDFCOUNT_SHIFT)
576                        & pac::spi::TXRXDFCOUNT_MASK);
577                spi.CONTROL = control | pac::spi::CTRL_ENABLE_MASK;
578                core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst);
579
580                // Flush the receive FIFO - needs volatile read
581                while (core::ptr::read_volatile(&spi.STATUS) & pac::spi::RX_FIFO_EMPTY_MASK) == 0 {
582                    let _ = core::ptr::read_volatile(&spi.RX_DATA);
583                }
584
585                for i in 0..remaining_bytes {
586                    // Wait until transmit FIFO is not full - needs volatile read
587                    while (core::ptr::read_volatile(&spi.STATUS) & pac::spi::TX_FIFO_FULL_MASK) != 0
588                    {
589                        core::hint::spin_loop();
590                    }
591                    if i < write.len() {
592                        // TX_DATA write needs to be volatile
593                        core::ptr::write_volatile(&mut spi.TX_DATA, write[i] as u32);
594                    }
595                    // Status and RX_DATA reads need to be volatile
596                    while (core::ptr::read_volatile(&spi.STATUS) & pac::spi::RX_FIFO_EMPTY_MASK)
597                        != 0
598                    {
599                        core::hint::spin_loop();
600                    }
601                    if i < read.len() {
602                        read[i] = core::ptr::read_volatile(&spi.RX_DATA) as u8;
603                    }
604                }
605
606                // Wait until the transfer is done - needs volatile read
607                while (core::ptr::read_volatile(&spi.STATUS) & pac::spi::ACTIVE_MASK) != 0 {
608                    core::hint::spin_loop();
609                }
610
611                // Reset state - single write is fine
612                spi.COMMAND |= pac::spi::TX_FIFO_RESET_MASK | pac::spi::RX_FIFO_RESET_MASK;
613                spi.CONTROL &= !pac::spi::CTRL_ENABLE_MASK;
614                core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst);
615            }
616        }
617
618        Ok(())
619    }
620
621    fn transfer_in_place(&mut self, data: &mut [u8]) -> Result<(), SpiError> {
622        if data.is_empty() {
623            return Ok(());
624        }
625
626        let buffer_aligned: bool = data.as_ptr().align_offset(4) == 0 && data.len() > 3;
627        trace!(
628            "Transferring in place, buffer aligned? {}; Data: {:x?}",
629            buffer_aligned,
630            data
631        );
632
633        unsafe {
634            let spi = &mut (*(*self.peripheral.address()).hw_reg);
635            let word_count = if buffer_aligned { data.len() / 4 } else { 0 } as u32;
636
637            if word_count > 0 {
638                let words = data.as_ptr() as *mut u32;
639
640                // Setup registers - single volatile write for initial setup
641                spi.FRAMESIZE = 32;
642                spi.FRAMESUP = word_count as u32 & pac::spi::BYTESUPPER_MASK;
643                let control = (spi.CONTROL & !pac::spi::TXRXDFCOUNT_MASK)
644                    | ((word_count << pac::spi::TXRXDFCOUNT_SHIFT) & pac::spi::TXRXDFCOUNT_MASK);
645                spi.CONTROL = control | pac::spi::CTRL_ENABLE_MASK;
646                core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst);
647
648                // Flush the receive FIFO - needs volatile read
649                while (core::ptr::read_volatile(&spi.STATUS) & pac::spi::RX_FIFO_EMPTY_MASK) == 0 {
650                    let _ = core::ptr::read_volatile(&spi.RX_DATA);
651                }
652
653                for i in 0..(word_count as usize) {
654                    // Status check needs volatile read
655                    while (core::ptr::read_volatile(&spi.STATUS) & pac::spi::TX_FIFO_FULL_MASK) != 0
656                    {
657                        core::hint::spin_loop();
658                    }
659                    // TX_DATA write needs to be volatile
660                    core::ptr::write_volatile(&mut spi.TX_DATA, (*words.add(i)).to_be());
661
662                    // Status and RX_DATA reads need to be volatile
663                    while (core::ptr::read_volatile(&spi.STATUS) & pac::spi::RX_FIFO_EMPTY_MASK)
664                        != 0
665                    {
666                        core::hint::spin_loop();
667                    }
668                    *words.add(i) = core::ptr::read_volatile(&spi.RX_DATA).to_be();
669                }
670
671                // Wait until transfer done - needs volatile read
672                while (core::ptr::read_volatile(&spi.STATUS) & pac::spi::ACTIVE_MASK) != 0 {
673                    core::hint::spin_loop();
674                }
675
676                // Reset state - single write is fine
677                spi.COMMAND |= pac::spi::TX_FIFO_RESET_MASK | pac::spi::RX_FIFO_RESET_MASK;
678                spi.CONTROL &= !pac::spi::CTRL_ENABLE_MASK;
679                core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst);
680            }
681
682            let data = &mut data[word_count as usize * 4..];
683
684            if !data.is_empty() {
685                // Setup registers - single volatile write for initial setup
686                spi.FRAMESIZE = 8;
687                spi.FRAMESUP = data.len() as u32 & pac::spi::BYTESUPPER_MASK;
688                let control = (spi.CONTROL & !pac::spi::TXRXDFCOUNT_MASK)
689                    | (((data.len() as u32) << pac::spi::TXRXDFCOUNT_SHIFT)
690                        & pac::spi::TXRXDFCOUNT_MASK);
691                spi.CONTROL = control | pac::spi::CTRL_ENABLE_MASK;
692                core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst);
693
694                // Flush the receive FIFO - needs volatile read
695                while (core::ptr::read_volatile(&spi.STATUS) & pac::spi::RX_FIFO_EMPTY_MASK) == 0 {
696                    let _ = core::ptr::read_volatile(&spi.RX_DATA);
697                }
698
699                for i in 0..data.len() {
700                    // Status check needs volatile read
701                    while (core::ptr::read_volatile(&spi.STATUS) & pac::spi::TX_FIFO_FULL_MASK) != 0
702                    {
703                        core::hint::spin_loop();
704                    }
705                    // TX_DATA write needs to be volatile
706                    core::ptr::write_volatile(&mut spi.TX_DATA, data[i] as u32);
707
708                    // Status and RX_DATA reads need to be volatile
709                    while (core::ptr::read_volatile(&spi.STATUS) & pac::spi::RX_FIFO_EMPTY_MASK)
710                        != 0
711                    {
712                        core::hint::spin_loop();
713                    }
714                    data[i] = core::ptr::read_volatile(&spi.RX_DATA) as u8;
715                }
716
717                // Wait until transfer done - needs volatile read
718                while (core::ptr::read_volatile(&spi.STATUS) & pac::spi::ACTIVE_MASK) != 0 {
719                    core::hint::spin_loop();
720                }
721
722                // Reset state - single write is fine
723                spi.COMMAND |= pac::spi::TX_FIFO_RESET_MASK | pac::spi::RX_FIFO_RESET_MASK;
724                spi.CONTROL &= !pac::spi::CTRL_ENABLE_MASK;
725                core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst);
726            }
727        }
728
729        Ok(())
730    }
731}