Skip to main content

mpfs_hal/
qspi.rs

1use crate::pac;
2use embassy_embedded_hal::SetConfig;
3use embedded_hal::spi::{Phase, Polarity};
4
5static mut QSPI_TAKEN: bool = false;
6
7pub fn init() {
8    unsafe {
9        pac::mss_config_clk_rst(
10            pac::mss_peripherals__MSS_PERIPH_QSPIXIP,
11            pac::MPFS_HAL_FIRST_HART as u8,
12            pac::PERIPH_RESET_STATE__PERIPHERAL_ON,
13        );
14        pac::MSS_QSPI_init();
15    }
16}
17
18pub struct Qspi {
19    _private: (),
20}
21
22impl crate::Peripheral for Qspi {
23    fn take() -> Option<Self> {
24        critical_section::with(|_| unsafe {
25            if QSPI_TAKEN {
26                None
27            } else {
28                QSPI_TAKEN = true;
29                Some(Self { _private: () })
30            }
31        })
32    }
33
34    unsafe fn steal() -> Self {
35        Self { _private: () }
36    }
37}
38
39impl embedded_hal::spi::ErrorType for Qspi {
40    type Error = SpiError;
41}
42
43#[derive(Debug)]
44pub enum SpiError {}
45
46impl embedded_hal::spi::Error for SpiError {
47    fn kind(&self) -> embedded_hal::spi::ErrorKind {
48        embedded_hal::spi::ErrorKind::Other
49    }
50}
51
52#[derive(Debug, Clone, Copy)]
53pub struct SpiConfig {
54    pub frequency: SpiFrequency,
55    pub phase: Phase,
56    pub polarity: Polarity,
57}
58
59impl Default for SpiConfig {
60    fn default() -> Self {
61        Self {
62            frequency: SpiFrequency::F5_000_000,
63            phase: Phase::CaptureOnFirstTransition,
64            polarity: Polarity::IdleLow,
65        }
66    }
67}
68
69#[derive(Debug, Clone, Copy)]
70#[repr(u8)]
71pub enum SpiFrequency {
72    F75_000_000 = 1,  // 150MHz / 2
73    F37_500_000 = 2,  // 150MHz / 4
74    F25_000_000 = 3,  // 150MHz / 6
75    F18_750_000 = 4,  // 150MHz / 8
76    F15_000_000 = 5,  // 150MHz / 10
77    F12_500_000 = 6,  // 150MHz / 12
78    F10_714_285 = 7,  // 150MHz / 14
79    F9_375_000 = 8,   // 150MHz / 16
80    F8_333_333 = 9,   // 150MHz / 18
81    F7_500_000 = 0xA, // 150MHz / 20
82    F6_818_181 = 0xB, // 150MHz / 22
83    F6_250_000 = 0xC, // 150MHz / 24
84    F5_769_230 = 0xD, // 150MHz / 26
85    F5_357_142 = 0xE, // 150MHz / 28
86    F5_000_000 = 0xF, // 150MHz / 30
87}
88
89impl SetConfig for Qspi {
90    type Config = SpiConfig;
91    type ConfigError = ();
92    fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> {
93        unsafe {
94            let sample = if config.phase == Phase::CaptureOnFirstTransition {
95                pac::MSS_QSPI_SAMPLE_POSAGE_SPICLK
96            } else {
97                pac::MSS_QSPI_SAMPLE_NEGAGE_SPICLK
98            };
99            let mode = if config.polarity == Polarity::IdleLow {
100                pac::mss_qspi_protocol_mode_t_MSS_QSPI_MODE0
101            } else {
102                pac::mss_qspi_protocol_mode_t_MSS_QSPI_MODE3
103            };
104            let value = (sample << pac::CTRL_SAMPLE)
105                | ((config.frequency as u32) << pac::CTRL_CLKRATE)
106                | (pac::mss_qspi_io_format_t_MSS_QSPI_NORMAL << pac::CTRL_QMODE12)
107                | (mode << pac::CTRL_CLKIDL)
108                | (0 << pac::CTRL_XIP)
109                | (0 << pac::CTRL_XIPADDR)
110                | pac::CTRL_EN_MASK;
111            trace!("QSPI Control Register: {:#032b}", value);
112            (*pac::QSPI).CONTROL = value;
113        }
114        Ok(())
115    }
116}
117
118/// When buffers are 4 byte aligned, QSPI can read and write 32-bit words. Otherwise, the 8-bit
119/// interface is used.
120impl embedded_hal::spi::SpiBus<u8> for Qspi {
121    fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
122        let buffer_aligned: bool = words.as_ptr().align_offset(4) == 0 && words.len() > 3;
123        trace!("Reading from QSPI. Buffer aligned: {:?}", buffer_aligned);
124        unsafe {
125            // Wait until QSPI is ready - needs volatile read
126            while (core::ptr::read_volatile(&(*pac::QSPI).STATUS) & pac::STTS_READY_MASK) == 0 {
127                core::hint::spin_loop();
128            }
129
130            // Disable interrupts
131            (*pac::QSPI).INTENABLE = 0;
132
133            // Configure frame size
134            let total_bytes = words.len();
135            (*pac::QSPI).FRAMESUP = (total_bytes as u32) & pac::FRMS_UBYTES_MASK;
136
137            // Configure frame control
138            let mut frame_ctrl = (total_bytes as u32) & 0xFFFF;
139            frame_ctrl |= 0 << pac::FRMS_CBYTES; // Set command bytes to 0, which will write and read whenever something is in the TX FIFO
140            frame_ctrl |= ((*pac::QSPI).CONTROL & pac::CTRL_QMODE12_MASK) << pac::FRMS_QSPI; // If set to QSPI mode, set QSPI bit
141
142            frame_ctrl |= if buffer_aligned {
143                pac::FRMS_FWORD_MASK
144            } else {
145                pac::FRMS_FBYTE_MASK
146            };
147            (*pac::QSPI).FRAMES = frame_ctrl;
148            core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst);
149
150            let word_count = if buffer_aligned { total_bytes / 4 } else { 0 };
151            if buffer_aligned {
152                // Enable 32-bit transfer mode
153                (*pac::QSPI).CONTROL |= pac::CTRL_FLAGSX4_MASK;
154
155                // Transfer 32-bit aligned words
156                let words_32 = words.as_ptr() as *mut u32;
157
158                for i in 0..word_count {
159                    // Wait until transmit FIFO is not full - needs volatile read
160                    while (core::ptr::read_volatile(&(*pac::QSPI).STATUS) & pac::STTS_TFFULL_MASK)
161                        != 0
162                    {
163                        core::hint::spin_loop();
164                    }
165                    // Send dummy data - needs volatile write
166                    core::ptr::write_volatile(&mut (*pac::QSPI).TXDATAX4, 0xFFFFFFFF);
167
168                    // Wait until receive FIFO is not empty - needs volatile read
169                    while (core::ptr::read_volatile(&(*pac::QSPI).STATUS) & pac::STTS_RFEMPTY_MASK)
170                        != 0
171                    {
172                        core::hint::spin_loop();
173                    }
174                    *words_32.add(i) = core::ptr::read_volatile(&(*pac::QSPI).RXDATAX4);
175                }
176
177                // Disable 32-bit transfer mode
178                (*pac::QSPI).CONTROL &= !pac::CTRL_FLAGSX4_MASK;
179
180                if total_bytes % 4 != 0 {
181                    // Without this delay the TXDATAX1 FIFO does not get updated with proper data
182                    pac::sleep_ms(10);
183                }
184            }
185
186            // Transfer remaining bytes
187            let remaining_start = word_count * 4;
188            for i in remaining_start..total_bytes {
189                // Wait until transmit FIFO is not full - needs volatile read
190                while (core::ptr::read_volatile(&(*pac::QSPI).STATUS) & pac::STTS_TFFULL_MASK) != 0
191                {
192                    core::hint::spin_loop();
193                }
194                // Send dummy data - needs volatile write
195                core::ptr::write_volatile(&mut (*pac::QSPI).TXDATAX1, 0xFF);
196
197                // Wait until receive FIFO is not empty - needs volatile read
198                while (core::ptr::read_volatile(&(*pac::QSPI).STATUS) & pac::STTS_RFEMPTY_MASK) != 0
199                {
200                    core::hint::spin_loop();
201                }
202                words[i] = core::ptr::read_volatile(&(*pac::QSPI).RXDATAX1);
203            }
204            // Make sure the read is complete (but we shouldn't get here) - needs volatile read
205            while (core::ptr::read_volatile(&(*pac::QSPI).STATUS) & pac::STTS_RDONE_MASK) == 0 {
206                warn!("Warning: read not complete");
207                if (core::ptr::read_volatile(&(*pac::QSPI).STATUS) & pac::STTS_FLAGSX4_MASK) != 0 {
208                    core::ptr::read_volatile(&(*pac::QSPI).RXDATAX4);
209                } else {
210                    core::ptr::read_volatile(&(*pac::QSPI).RXDATAX1);
211                }
212            }
213        }
214        trace!("QSPI read complete: {:x?}", words);
215        Ok(())
216    }
217
218    fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
219        let buffer_aligned: bool = words.as_ptr().align_offset(4) == 0 && words.len() > 3;
220        trace!(
221            "Writing to QSPI {:x?}. Buffer aligned: {:?}",
222            words,
223            buffer_aligned
224        );
225        unsafe {
226            // Wait until QSPI is ready - needs volatile read
227            while (core::ptr::read_volatile(&(*pac::QSPI).STATUS) & pac::STTS_READY_MASK) == 0 {
228                core::hint::spin_loop();
229            }
230
231            // Disable interrupts
232            (*pac::QSPI).INTENABLE = 0;
233
234            // Configure frame size
235            let total_bytes = words.len();
236            (*pac::QSPI).FRAMESUP = (total_bytes as u32) & pac::FRMS_UBYTES_MASK;
237
238            // Configure frame control
239            let mut frame_ctrl = (total_bytes as u32) & 0xFFFF;
240            frame_ctrl |= 0 << pac::FRMS_CBYTES; // Set command bytes to 0, which will write and read whenever something is in the TX FIFO
241            frame_ctrl |= ((*pac::QSPI).CONTROL & pac::CTRL_QMODE12_MASK) << pac::FRMS_QSPI; // If set to QSPI mode, set QSPI bit
242
243            frame_ctrl |= pac::FRMS_FWORD_MASK; // Set full word mode
244            (*pac::QSPI).FRAMES = frame_ctrl;
245            core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst);
246
247            let word_count = if buffer_aligned { total_bytes / 4 } else { 0 };
248            if buffer_aligned {
249                // Enable 32-bit transfer mode
250                (*pac::QSPI).CONTROL |= pac::CTRL_FLAGSX4_MASK;
251
252                // Transfer 32-bit aligned words
253                let words_32 = words.as_ptr() as *const u32;
254
255                for i in 0..word_count {
256                    // Wait until transmit FIFO is not full - needs volatile read
257                    while (core::ptr::read_volatile(&(*pac::QSPI).STATUS) & pac::STTS_TFFULL_MASK)
258                        != 0
259                    {
260                        core::hint::spin_loop();
261                    }
262                    // TX_DATA write needs to be volatile
263                    core::ptr::write_volatile(&mut (*pac::QSPI).TXDATAX4, *words_32.add(i));
264                }
265
266                // Disable 32-bit transfer mode
267                (*pac::QSPI).CONTROL &= !pac::CTRL_FLAGSX4_MASK;
268
269                if total_bytes % 4 != 0 {
270                    // Without this delay the TXDATAX1 FIFO does not get updated with proper data
271                    pac::sleep_ms(10);
272                }
273            }
274
275            // Transfer remaining bytes
276            let remaining_start = word_count * 4;
277            for i in remaining_start..total_bytes {
278                // Wait until transmit FIFO is not full - needs volatile read
279                while (core::ptr::read_volatile(&(*pac::QSPI).STATUS) & pac::STTS_TFFULL_MASK) != 0
280                {
281                    core::hint::spin_loop();
282                }
283                // TX_DATA write needs to be volatile
284                core::ptr::write_volatile(&mut (*pac::QSPI).TXDATAX1, words[i]);
285            }
286            // Wait until transfer done - needs volatile read
287            while (core::ptr::read_volatile(&(*pac::QSPI).STATUS) & pac::STTS_TDONE_MASK) == 0 {
288                core::hint::spin_loop();
289            }
290        }
291        trace!("QSPI write complete");
292        Ok(())
293    }
294
295    fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
296        let buffer_aligned: bool = write.as_ptr().align_offset(4) == 0
297            && read.as_ptr().align_offset(4) == 0
298            && (write.len() > 3 || read.len() > 3);
299        trace!(
300            "QSPI transfer {:x?}. Buffer aligned: {:?}",
301            write,
302            buffer_aligned
303        );
304        unsafe {
305            // Wait until QSPI is ready - needs volatile read
306            while (core::ptr::read_volatile(&(*pac::QSPI).STATUS) & pac::STTS_READY_MASK) == 0 {
307                core::hint::spin_loop();
308            }
309
310            // Disable interrupts
311            (*pac::QSPI).INTENABLE = 0;
312
313            // Configure frame size
314            let total_bytes = read.len().max(write.len());
315            (*pac::QSPI).FRAMESUP = (total_bytes as u32) & pac::FRMS_UBYTES_MASK;
316
317            // Configure frame control
318            let mut frame_ctrl = (total_bytes as u32) & 0xFFFF;
319            frame_ctrl |= 0 << pac::FRMS_CBYTES; // Set command bytes to 0, which will write and read whenever something is in the TX FIFO
320            frame_ctrl |= ((*pac::QSPI).CONTROL & pac::CTRL_QMODE12_MASK) << pac::FRMS_QSPI; // If set to QSPI mode, set QSPI bit
321
322            frame_ctrl |= if buffer_aligned {
323                pac::FRMS_FWORD_MASK
324            } else {
325                pac::FRMS_FBYTE_MASK
326            };
327            (*pac::QSPI).FRAMES = frame_ctrl;
328            core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst);
329
330            // Enable 32-bit transfer mode
331            (*pac::QSPI).CONTROL |= pac::CTRL_FLAGSX4_MASK;
332
333            // Transfer 32-bit aligned words
334            let write_32 = write.as_ptr() as *const u32;
335            let read_32 = read.as_ptr() as *mut u32;
336            let word_count = if buffer_aligned { total_bytes / 4 } else { 0 };
337            let tx_word_count = write.len() / 4;
338            let rx_word_count = read.len() / 4;
339            if buffer_aligned {
340                for i in 0..word_count {
341                    // Wait until transmit FIFO is not full - needs volatile read
342                    while (core::ptr::read_volatile(&(*pac::QSPI).STATUS) & pac::STTS_TFFULL_MASK)
343                        != 0
344                    {
345                        core::hint::spin_loop();
346                    }
347                    if i < tx_word_count {
348                        // Write data - needs volatile write
349                        core::ptr::write_volatile(&mut (*pac::QSPI).TXDATAX4, *write_32.add(i));
350                    } else {
351                        // Send dummy data - needs volatile write
352                        core::ptr::write_volatile(&mut (*pac::QSPI).TXDATAX4, 0xFFFFFFFF);
353                    }
354
355                    // Wait until receive FIFO is not empty - needs volatile read
356                    while (core::ptr::read_volatile(&(*pac::QSPI).STATUS) & pac::STTS_RFEMPTY_MASK)
357                        != 0
358                    {
359                        core::hint::spin_loop();
360                    }
361                    if i < rx_word_count {
362                        *read_32.add(i) = core::ptr::read_volatile(&(*pac::QSPI).RXDATAX4);
363                    } else {
364                        core::ptr::read_volatile(&(*pac::QSPI).RXDATAX1); // discard
365                    }
366                }
367
368                // Disable 32-bit transfer mode
369                (*pac::QSPI).CONTROL &= !pac::CTRL_FLAGSX4_MASK;
370
371                if total_bytes % 4 != 0 {
372                    // Without this delay the TXDATAX1 FIFO does not get updated with proper data
373                    pac::sleep_ms(10);
374                }
375            }
376
377            // Transfer remaining bytes
378            let remaining_start = word_count * 4;
379            for i in remaining_start..total_bytes {
380                // Wait until transmit FIFO is not full - needs volatile read
381                while (core::ptr::read_volatile(&(*pac::QSPI).STATUS) & pac::STTS_TFFULL_MASK) != 0
382                {
383                    core::hint::spin_loop();
384                }
385                if i < write.len() {
386                    // Write data - needs volatile write
387                    core::ptr::write_volatile(&mut (*pac::QSPI).TXDATAX1, write[i]);
388                } else {
389                    // Send dummy data - needs volatile write
390                    core::ptr::write_volatile(&mut (*pac::QSPI).TXDATAX1, 0xFF);
391                }
392
393                // Wait until receive FIFO is not empty - needs volatile read
394                while (core::ptr::read_volatile(&(*pac::QSPI).STATUS) & pac::STTS_RFEMPTY_MASK) != 0
395                {
396                    core::hint::spin_loop();
397                }
398                if i < read.len() {
399                    read[i] = core::ptr::read_volatile(&(*pac::QSPI).RXDATAX1);
400                } else {
401                    core::ptr::read_volatile(&(*pac::QSPI).RXDATAX1); // discard
402                }
403            }
404            // Make sure the read is complete (but we shouldn't get here) - needs volatile read
405            while (core::ptr::read_volatile(&(*pac::QSPI).STATUS) & pac::STTS_RDONE_MASK) == 0 {
406                warn!("Warning: read not complete");
407                if (core::ptr::read_volatile(&(*pac::QSPI).STATUS) & pac::STTS_FLAGSX4_MASK) != 0 {
408                    core::ptr::read_volatile(&(*pac::QSPI).RXDATAX4);
409                } else {
410                    core::ptr::read_volatile(&(*pac::QSPI).RXDATAX1);
411                }
412            }
413        }
414        trace!("QSPI transfer received {:x?}", read);
415        Ok(())
416    }
417
418    fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
419        let buffer_aligned: bool = words.as_ptr().align_offset(4) == 0 && words.len() > 3;
420        trace!(
421            "QSPI transfer_in_place {:x?}. Buffer aligned: {:?}",
422            words,
423            buffer_aligned
424        );
425        unsafe {
426            // Wait until QSPI is ready - needs volatile read
427            while (core::ptr::read_volatile(&(*pac::QSPI).STATUS) & pac::STTS_READY_MASK) == 0 {
428                core::hint::spin_loop();
429            }
430
431            // Disable interrupts
432            (*pac::QSPI).INTENABLE = 0;
433
434            // Configure frame size
435            let total_bytes = words.len();
436            (*pac::QSPI).FRAMESUP = (total_bytes as u32) & pac::FRMS_UBYTES_MASK;
437
438            // Configure frame control
439            let mut frame_ctrl = (total_bytes as u32) & 0xFFFF;
440            frame_ctrl |= 0 << pac::FRMS_CBYTES; // Set command bytes to 0, which will write and read whenever something is in the TX FIFO
441            frame_ctrl |= ((*pac::QSPI).CONTROL & pac::CTRL_QMODE12_MASK) << pac::FRMS_QSPI; // If set to QSPI mode, set QSPI bit
442
443            frame_ctrl |= if buffer_aligned {
444                pac::FRMS_FWORD_MASK
445            } else {
446                pac::FRMS_FBYTE_MASK
447            };
448            (*pac::QSPI).FRAMES = frame_ctrl;
449            core::sync::atomic::fence(core::sync::atomic::Ordering::SeqCst);
450
451            let word_count = if buffer_aligned { total_bytes / 4 } else { 0 };
452            if buffer_aligned {
453                // Enable 32-bit transfer mode
454                (*pac::QSPI).CONTROL |= pac::CTRL_FLAGSX4_MASK;
455
456                // Transfer 32-bit aligned words
457                let words_32 = words.as_ptr() as *mut u32;
458
459                for i in 0..word_count {
460                    // Wait until transmit FIFO is not full - needs volatile read
461                    while (core::ptr::read_volatile(&(*pac::QSPI).STATUS) & pac::STTS_TFFULL_MASK)
462                        != 0
463                    {
464                        core::hint::spin_loop();
465                    }
466                    // TX_DATA write needs to be volatile
467                    core::ptr::write_volatile(&mut (*pac::QSPI).TXDATAX4, *words_32.add(i));
468
469                    // Wait until receive FIFO is not empty - needs volatile read
470                    while (core::ptr::read_volatile(&(*pac::QSPI).STATUS) & pac::STTS_RFEMPTY_MASK)
471                        != 0
472                    {
473                        core::hint::spin_loop();
474                    }
475                    *words_32.add(i) = core::ptr::read_volatile(&(*pac::QSPI).RXDATAX4);
476                }
477
478                // Disable 32-bit transfer mode
479                (*pac::QSPI).CONTROL &= !pac::CTRL_FLAGSX4_MASK;
480
481                if total_bytes % 4 != 0 {
482                    // Without this delay the TXDATAX1 FIFO does not get updated with proper data
483                    pac::sleep_ms(10);
484                }
485            }
486
487            // Transfer remaining bytes
488            let remaining_start = word_count * 4;
489            for i in remaining_start..total_bytes {
490                // Wait until transmit FIFO is not full - needs volatile read
491                while (core::ptr::read_volatile(&(*pac::QSPI).STATUS) & pac::STTS_TFFULL_MASK) != 0
492                {
493                    core::hint::spin_loop();
494                }
495                // TX_DATA write needs to be volatile
496                core::ptr::write_volatile(&mut (*pac::QSPI).TXDATAX1, words[i]);
497
498                // Wait until receive FIFO is not empty - needs volatile read
499                while (core::ptr::read_volatile(&(*pac::QSPI).STATUS) & pac::STTS_RFEMPTY_MASK) != 0
500                {
501                    core::hint::spin_loop();
502                }
503                words[i] = core::ptr::read_volatile(&(*pac::QSPI).RXDATAX1);
504            }
505            // Make sure the read is complete (but we shouldn't get here) - needs volatile read
506            while (core::ptr::read_volatile(&(*pac::QSPI).STATUS) & pac::STTS_RDONE_MASK) == 0 {
507                warn!("Warning: read not complete");
508                if (core::ptr::read_volatile(&(*pac::QSPI).STATUS) & pac::STTS_FLAGSX4_MASK) != 0 {
509                    core::ptr::read_volatile(&(*pac::QSPI).RXDATAX4);
510                } else {
511                    core::ptr::read_volatile(&(*pac::QSPI).RXDATAX1);
512                }
513            }
514        }
515        trace!("QSPI transfer_in_place received {:x?}", words);
516        Ok(())
517    }
518
519    fn flush(&mut self) -> Result<(), Self::Error> {
520        Ok(())
521    }
522}
523
524// TODO: Create an async implementation
525impl embedded_hal_async::spi::SpiBus<u8> for Qspi {
526    async fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
527        embedded_hal::spi::SpiBus::read(self, words)
528    }
529
530    async fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
531        embedded_hal::spi::SpiBus::write(self, words)
532    }
533
534    async fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
535        embedded_hal::spi::SpiBus::transfer(self, read, write)
536    }
537
538    async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
539        embedded_hal::spi::SpiBus::transfer_in_place(self, words)
540    }
541
542    async fn flush(&mut self) -> Result<(), Self::Error> {
543        embedded_hal::spi::SpiBus::flush(self)
544    }
545}
546
547// A SPI implementation that uses direct control. Used to create a 400kHz clock.
548// Doesn't seem to be needed.
549/*
550pub struct QspiDirect {}
551
552impl embedded_hal::spi::ErrorType for QspiDirect {
553    type Error = SdError;
554}
555
556impl QspiDirect {
557    pub fn new() -> Self {
558        // Enable direct control of SSEL, SCLK, and SDO[0]
559        unsafe {
560            (*pac::QSPI).DIRECT = (1 << pac::DIRECT_EN_SCLK)   // Enable SCLK control
561                                | (1 << pac::DIRECT_EN_SDO) // Enable SDO[0] control
562                                | (1 << pac::DIRECT_OP_SDOE) // Enable SDO[0] control
563        }
564
565        Self {}
566    }
567}
568
569impl embedded_hal_async::spi::SpiBus<u8> for QspiDirect {
570    async fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
571        // 400kHz = 2.5µs per cycle, so 1.25µs per half cycle
572        const QUARTER_CYCLE_NANOS: u64 = 625;
573
574        for byte in words.iter_mut() {
575            let mut received = 0u8;
576
577            // Transfer one byte, bit by bit
578            for bit in (0..8).rev() {
579                let start = embassy_time::Instant::now();
580
581                // Set MOSI (SDO[0])
582                let bit_out = (*byte >> bit) & 1;
583                unsafe {
584                    let mut reg = (*pac::QSPI).DIRECT;
585                    reg &= !(1 << pac::DIRECT_OP_SDO); // Clear only SDO[0] bit
586                    reg |= (bit_out as u32) << pac::DIRECT_OP_SDO; // Set new SDO[0] value
587                    (*pac::QSPI).DIRECT = reg;
588
589                    // Spin until quarter cycle (before clock high)
590                    while start.elapsed().as_ticks() < QUARTER_CYCLE_NANOS {
591                        core::hint::spin_loop();
592                    }
593
594                    // Clock high
595                    (*pac::QSPI).DIRECT |= 1 << pac::DIRECT_OP_SCLK;
596
597                    // Spin until half cycle + quarter cycle (before sampling)
598                    while start.elapsed().as_ticks() < 2 * QUARTER_CYCLE_NANOS {
599                        core::hint::spin_loop();
600                    }
601
602                    // Sample MISO (SDI[1])
603                    let raw_direct = (*pac::QSPI).DIRECT;
604                    let sdi = (raw_direct >> (pac::DIRECT_IP_SDI + 1)) & 1;
605                    received |= (sdi as u8) << bit;
606
607                    // Spin until full cycle + quarter cycle (before clock low)
608                    while start.elapsed().as_ticks() < 3 * QUARTER_CYCLE_NANOS {
609                        core::hint::spin_loop();
610                    }
611
612                    // Clock low
613                    (*pac::QSPI).DIRECT &= !(1 << pac::DIRECT_OP_SCLK);
614
615                    // Spin until end of cycle
616                    while start.elapsed().as_ticks() < 4 * QUARTER_CYCLE_NANOS {
617                        core::hint::spin_loop();
618                    }
619                }
620            }
621
622            *byte = received;
623        }
624
625        Ok(())
626    }
627
628    async fn read(&mut self, _words: &mut [u8]) -> Result<(), Self::Error> {
629        unimplemented!()
630    }
631
632    async fn write(&mut self, _words: &[u8]) -> Result<(), Self::Error> {
633        unimplemented!()
634    }
635
636    async fn transfer(&mut self, _read: &mut [u8], _write: &[u8]) -> Result<(), Self::Error> {
637        unimplemented!()
638    }
639
640    async fn flush(&mut self) -> Result<(), Self::Error> {
641        unimplemented!()
642    }
643}
644*/