Skip to main content

esp_hal/spi/master/low_level/
mod.rs

1#[cfg(spi_master_version = "1")]
2use core::cell::Cell;
3use core::{
4    cell::UnsafeCell,
5    future::Future,
6    mem::MaybeUninit,
7    pin::Pin,
8    sync::atomic::{AtomicUsize, Ordering},
9    task::{Context, Poll},
10};
11
12use enumset::{EnumSet, enum_set};
13
14use super::{
15    Address,
16    AnySpi,
17    Command,
18    Config,
19    ConfigError,
20    DataMode,
21    EMPTY_WRITE_PAD,
22    FIFO_SIZE,
23    SpiInterrupt,
24    SpiPinGuard,
25    any,
26};
27use crate::{
28    asynch::AtomicWaker,
29    clock::ll::SpiInstance,
30    gpio::{InputSignal, OutputSignal},
31    handler,
32    interrupt::InterruptHandler,
33    pac::spi2::RegisterBlock,
34    private::{self, DropGuard},
35    ram,
36    spi::{BitOrder, Error, Mode},
37    system::PeripheralGuard,
38};
39
40#[cfg_attr(spi_master_version = "1", path = "v1.rs")]
41#[cfg_attr(spi_master_version = "2", path = "v2.rs")]
42#[cfg_attr(spi_master_version = "3", path = "v3.rs")]
43mod version;
44
45#[derive(Debug)]
46#[cfg_attr(feature = "defmt", derive(defmt::Format))]
47pub(super) struct SpiWrapper<'d> {
48    pub(super) spi: AnySpi<'d>,
49    _guard: PeripheralGuard,
50}
51
52impl<'d> SpiWrapper<'d> {
53    pub(super) fn new(spi: impl Instance + 'd) -> Self {
54        let p = spi.info().peripheral;
55        let this = Self {
56            spi: spi.degrade(),
57            _guard: PeripheralGuard::new(p),
58        };
59
60        // Initialize state
61        unsafe {
62            this.state()
63                .pins
64                .get()
65                .write(MaybeUninit::new(SpiPinGuard::new_unconnected()))
66        }
67
68        this
69    }
70
71    pub(super) fn info(&self) -> &'static Info {
72        self.spi.info()
73    }
74
75    pub(super) fn state(&self) -> &'static State {
76        self.spi.state()
77    }
78
79    pub(super) fn disable_peri_interrupt_on_all_cores(&self) {
80        self.spi.disable_peri_interrupt_on_all_cores();
81    }
82
83    pub(super) fn set_interrupt_handler(&self, handler: InterruptHandler) {
84        self.spi.set_interrupt_handler(handler);
85    }
86
87    pub(super) fn pins(&mut self) -> &mut SpiPinGuard {
88        unsafe {
89            // SAFETY: we "own" the state, we are allowed to borrow it mutably
90            self.state().pins()
91        }
92    }
93}
94
95impl Drop for SpiWrapper<'_> {
96    fn drop(&mut self) {
97        unsafe {
98            // SAFETY: we "own" the state, we are allowed to deinit it
99            self.spi.state().deinit();
100        }
101    }
102}
103
104pub(super) struct SpiClockGuard {
105    clock: SpiInstance,
106}
107
108impl SpiClockGuard {
109    pub(super) fn new(spi: &Info) -> Self {
110        let clock = spi.clock_instance;
111        crate::clock::ll::ClockTree::with(|clocks| clock.request_function_clock(clocks));
112        Self { clock }
113    }
114}
115
116impl Drop for SpiClockGuard {
117    fn drop(&mut self) {
118        crate::clock::ll::ClockTree::with(|clocks| self.clock.release_function_clock(clocks));
119    }
120}
121
122/// SPI peripheral instance.
123pub trait Instance: private::Sealed + any::Degrade {
124    #[doc(hidden)]
125    /// Returns the peripheral data and state describing this instance.
126    fn parts(&self) -> (&'static Info, &'static State);
127
128    /// Returns the peripheral data describing this instance.
129    #[doc(hidden)]
130    #[inline(always)]
131    fn info(&self) -> &'static Info {
132        self.parts().0
133    }
134
135    /// Returns the peripheral state for this instance.
136    #[doc(hidden)]
137    #[inline(always)]
138    fn state(&self) -> &'static State {
139        self.parts().1
140    }
141}
142
143/// Marker trait for QSPI-capable SPI peripherals.
144#[doc(hidden)]
145pub trait QspiInstance: Instance {}
146
147/// Peripheral data describing a particular SPI instance.
148#[doc(hidden)]
149#[non_exhaustive]
150#[allow(private_interfaces, reason = "Unstable details")]
151pub struct Info {
152    /// Pointer to the register block for this SPI instance.
153    ///
154    /// Used with [`Self::register_block`] to access the register block.
155    pub register_block: *const RegisterBlock,
156
157    /// The system peripheral marker.
158    pub peripheral: crate::system::Peripheral,
159
160    /// Interrupt handler for the asynchronous operations.
161    pub async_handler: InterruptHandler,
162
163    /// SCLK signal.
164    pub sclk: OutputSignal,
165
166    /// Chip select signals.
167    pub cs: &'static [OutputSignal],
168
169    pub sio_inputs: &'static [InputSignal],
170    pub sio_outputs: &'static [OutputSignal],
171
172    /// Clocks tree instance for this SPI peripheral.
173    pub clock_instance: crate::soc::clocks::SpiInstance,
174}
175
176impl Info {
177    pub(super) fn cs(&self, n: usize) -> OutputSignal {
178        *unwrap!(self.cs.get(n), "CS{} is not defined", n)
179    }
180
181    pub(super) fn opt_sio_input(&self, n: usize) -> Option<InputSignal> {
182        self.sio_inputs.get(n).copied()
183    }
184
185    pub(super) fn opt_sio_output(&self, n: usize) -> Option<OutputSignal> {
186        self.sio_outputs.get(n).copied()
187    }
188
189    pub(super) fn sio_input(&self, n: usize) -> InputSignal {
190        unwrap!(self.opt_sio_input(n), "SIO{} is not defined", n)
191    }
192
193    pub(super) fn sio_output(&self, n: usize) -> OutputSignal {
194        unwrap!(self.opt_sio_output(n), "SIO{} is not defined", n)
195    }
196}
197
198pub(super) struct Driver {
199    pub(super) info: &'static Info,
200    pub(super) state: &'static State,
201}
202
203// Private implementation bits.
204impl Driver {
205    /// Returns the register block for this SPI instance.
206    pub(super) fn regs(&self) -> &RegisterBlock {
207        unsafe { &*self.info.register_block }
208    }
209
210    pub(super) fn abort_transfer(&self) {
211        version::abort_transfer(self);
212        self.update();
213    }
214
215    /// Initializes for full-duplex 1 bit mode.
216    pub(super) fn init(&self) {
217        version::enable_peripheral_clock(self);
218
219        crate::soc::clocks::ClockTree::with(|clocks| {
220            #[cfg(soc_clock_node_spi_function_clock_is_configurable)]
221            self.info.clock_instance.configure_function_clock(
222                clocks,
223                crate::soc::clocks::SpiFunctionClockConfig::default(),
224            );
225            self.info.clock_instance.request_function_clock(clocks);
226
227            self.regs().user().modify(|_, w| {
228                w.usr_miso_highpart().clear_bit();
229                w.usr_mosi_highpart().clear_bit();
230                w.doutdin().set_bit();
231                w.usr_miso().set_bit();
232                w.usr_mosi().set_bit();
233                w.cs_hold().set_bit();
234                w.usr_dummy_idle().set_bit();
235                w.usr_addr().clear_bit();
236                w.usr_command().clear_bit()
237            });
238
239            version::init(self);
240            self.info.clock_instance.release_function_clock(clocks);
241        });
242
243        self.regs().slave().write(|w| unsafe { w.bits(0) });
244    }
245
246    fn init_spi_data_mode(
247        &self,
248        cmd_mode: DataMode,
249        address_mode: DataMode,
250        data_mode: DataMode,
251    ) -> Result<(), Error> {
252        version::init_spi_data_mode(self, cmd_mode, address_mode, data_mode)
253    }
254
255    /// Enables or disables listening for the given interrupts.
256    #[cfg_attr(not(feature = "unstable"), allow(dead_code))]
257    pub(super) fn enable_listen(&self, interrupts: EnumSet<SpiInterrupt>, enable: bool) {
258        version::enable_listen(self, interrupts, enable);
259    }
260
261    /// Returns the asserted interrupts.
262    #[cfg_attr(not(feature = "unstable"), allow(dead_code))]
263    pub(super) fn interrupts(&self) -> EnumSet<SpiInterrupt> {
264        version::interrupts(self)
265    }
266
267    /// Resets asserted interrupts.
268    pub(super) fn clear_interrupts(&self, interrupts: EnumSet<SpiInterrupt>) {
269        version::clear_interrupts(self, interrupts);
270    }
271
272    pub(super) fn apply_config(&self, config: &Config) -> Result<(), ConfigError> {
273        config.validate()?;
274
275        let raw = config.raw_clock_reg_value()?;
276        crate::soc::clocks::ClockTree::with(|clocks| {
277            #[cfg(soc_clock_node_spi_function_clock_is_configurable)]
278            self.info
279                .clock_instance
280                .configure_function_clock(clocks, config.clock_source);
281            self.info.clock_instance.request_function_clock(clocks);
282
283            self.regs().clock().write(|w| unsafe { w.bits(raw) });
284
285            self.set_bit_order(config.read_bit_order, config.write_bit_order);
286            self.set_data_mode(config.mode);
287
288            version::apply_config(self);
289            self.info.clock_instance.release_function_clock(clocks);
290        });
291
292        self.state
293            .min_async_transfer_size
294            .store(config.min_async_transfer_size, Ordering::Relaxed);
295
296        Ok(())
297    }
298
299    fn set_data_mode(&self, data_mode: Mode) {
300        version::set_data_mode(self, data_mode);
301    }
302
303    #[cfg(not(spi_master_bit_order_is_bool))]
304    fn set_bit_order(&self, read_order: BitOrder, write_order: BitOrder) {
305        let read_value = match read_order {
306            BitOrder::MsbFirst => 0,
307            BitOrder::LsbFirst => 1,
308        };
309        let write_value = match write_order {
310            BitOrder::MsbFirst => 0,
311            BitOrder::LsbFirst => 1,
312        };
313        self.regs().ctrl().modify(|_, w| unsafe {
314            w.rd_bit_order().bits(read_value);
315            w.wr_bit_order().bits(write_value);
316            w
317        });
318    }
319
320    #[cfg(spi_master_bit_order_is_bool)]
321    fn set_bit_order(&self, read_order: BitOrder, write_order: BitOrder) {
322        let read_value = match read_order {
323            BitOrder::MsbFirst => false,
324            BitOrder::LsbFirst => true,
325        };
326        let write_value = match write_order {
327            BitOrder::MsbFirst => false,
328            BitOrder::LsbFirst => true,
329        };
330        self.regs().ctrl().modify(|_, w| {
331            w.rd_bit_order().bit(read_value);
332            w.wr_bit_order().bit(write_value);
333            w
334        });
335    }
336
337    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
338    pub(super) fn fill_fifo(&self, chunk: &[u8]) {
339        let (chunks, rem) = chunk.as_chunks::<4>();
340        let mut w_iter = self.regs().w_iter();
341        for c in chunks {
342            if let Some(w_reg) = w_iter.next() {
343                let word = u32::from_le_bytes(*c);
344                w_reg.write(|w| w.buf().set(word));
345            }
346        }
347        if !rem.is_empty()
348            && let Some(w_reg) = w_iter.next()
349        {
350            let word = match rem.len() {
351                3 => (rem[0] as u32) | ((rem[1] as u32) << 8) | ((rem[2] as u32) << 16),
352                2 => (rem[0] as u32) | ((rem[1] as u32) << 8),
353                1 => rem[0] as u32,
354                _ => unreachable!(),
355            };
356            w_reg.write(|w| w.buf().set(word));
357        }
358    }
359
360    /// Writes bytes to SPI.
361    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
362    pub(super) fn write_one(&self, words: &[u8]) -> Result<(), Error> {
363        if words.len() > FIFO_SIZE {
364            return Err(Error::FifoSizeExeeded);
365        }
366        self.configure_datalen(0, words.len());
367        self.fill_fifo(words);
368        self.start_operation();
369        Ok(())
370    }
371
372    /// Writes bytes to SPI.
373    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
374    pub(super) fn write(&self, words: &[u8]) -> Result<(), Error> {
375        for chunk in words.chunks(FIFO_SIZE) {
376            self.write_one(chunk)?;
377            self.flush()?;
378        }
379        Ok(())
380    }
381
382    /// Writes bytes to SPI.
383    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
384    pub(super) async fn write_async(&self, words: &[u8]) -> Result<(), Error> {
385        for chunk in words.chunks(FIFO_SIZE) {
386            self.write_one(chunk)?;
387            self.flush_async().await;
388        }
389        Ok(())
390    }
391
392    /// Reads bytes from SPI.
393    ///
394    /// Sends out a stuffing byte for every byte to read. Does not perform
395    /// flushing. To read the response to a prior write, use [`Self::transfer`]
396    /// instead.
397    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
398    pub(super) fn read(&self, words: &mut [u8]) -> Result<(), Error> {
399        let empty_array = [EMPTY_WRITE_PAD; FIFO_SIZE];
400
401        for chunk in words.chunks_mut(FIFO_SIZE) {
402            self.write_one(&empty_array[0..chunk.len()])?;
403            self.flush()?;
404            self.read_from_fifo(chunk)?;
405        }
406        Ok(())
407    }
408
409    /// Reads bytes from SPI.
410    ///
411    /// Sends out a stuffing byte for every byte to read. To read the response to a
412    /// prior write, use [`Self::transfer`] instead
413    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
414    pub(super) async fn read_async(&self, words: &mut [u8]) -> Result<(), Error> {
415        let empty_array = [EMPTY_WRITE_PAD; FIFO_SIZE];
416
417        for chunk in words.chunks_mut(FIFO_SIZE) {
418            self.write_one(&empty_array[0..chunk.len()])?;
419            self.flush_async().await;
420            self.read_from_fifo(chunk)?;
421        }
422        Ok(())
423    }
424
425    /// Reads received bytes from SPI FIFO.
426    ///
427    /// Copies the contents of the SPI receive FIFO into `words`. Does not perform
428    /// any data transfer. To read the response to a prior write, use
429    /// [`Self::transfer`] instead
430    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
431    pub(super) fn read_from_fifo(&self, words: &mut [u8]) -> Result<(), Error> {
432        if words.len() > FIFO_SIZE {
433            return Err(Error::FifoSizeExeeded);
434        }
435
436        for (chunk, w_reg) in words.chunks_mut(4).zip(self.regs().w_iter()) {
437            let reg_val = w_reg.read().bits();
438            let bytes = reg_val.to_le_bytes();
439
440            let len = chunk.len();
441            chunk.copy_from_slice(&bytes[..len]);
442        }
443
444        Ok(())
445    }
446
447    pub(super) fn busy(&self) -> bool {
448        self.regs().cmd().read().usr().bit_is_set()
449    }
450
451    // Check if the bus is busy and if it is wait for it to be idle
452    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
453    pub(super) fn flush_async(&self) -> impl Future<Output = ()> {
454        SpiFuture { driver: self }
455    }
456
457    // Check if the bus is busy and if it is wait for it to be idle
458    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
459    pub(super) fn flush(&self) -> Result<(), Error> {
460        while self.busy() {
461            // wait for bus to be clear
462        }
463        Ok(())
464    }
465
466    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
467    pub(super) fn transfer_in_place(&self, words: &mut [u8]) -> Result<(), Error> {
468        for chunk in words.chunks_mut(FIFO_SIZE) {
469            self.write_one(chunk)?;
470            self.flush()?;
471            self.read_from_fifo(chunk)?;
472        }
473
474        Ok(())
475    }
476
477    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
478    pub(super) fn transfer(&self, read: &mut [u8], write: &[u8]) -> Result<(), Error> {
479        let mut write_from = 0;
480        let mut read_from = 0;
481
482        loop {
483            // How many bytes we write in this chunk
484            let write_inc = core::cmp::min(FIFO_SIZE, write.len() - write_from);
485            // How many bytes we read in this chunk
486            let read_inc = core::cmp::min(FIFO_SIZE, read.len() - read_from);
487
488            if (write_inc == 0) && (read_inc == 0) {
489                break;
490            }
491
492            if write_inc < read_inc {
493                // Read more than we write, must pad writing part with zeros
494                let mut empty = [EMPTY_WRITE_PAD; FIFO_SIZE];
495                empty[0..write_inc].copy_from_slice(&write[write_from..][..write_inc]);
496                self.write_one(&empty[..read_inc])?;
497            } else {
498                self.write_one(&write[write_from..][..write_inc])?;
499            }
500
501            self.flush()?;
502
503            if read_inc > 0 {
504                self.read_from_fifo(&mut read[read_from..][..read_inc])?;
505            }
506
507            write_from += write_inc;
508            read_from += read_inc;
509        }
510        Ok(())
511    }
512
513    fn prepare_half_duplex_chunk(&self, first: bool, last: bool) {
514        self.regs().user().modify(|_, w| {
515            if !first {
516                w.usr_command().clear_bit();
517                w.usr_addr().clear_bit();
518                w.usr_dummy().clear_bit();
519                w.cs_setup().clear_bit();
520            }
521            w.cs_hold().bit(!last)
522        });
523        version::set_cs_keep_active(self, !last);
524    }
525
526    /// Blocking, FIFO-based half-duplex read.
527    ///
528    /// Performs the command, address, dummy, and data phases as a single SPI transaction without
529    /// involving the DMA engine. Transfers larger than the FIFO are split into chunks while keeping
530    /// CS asserted.
531    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
532    pub(super) fn half_duplex_read(
533        &self,
534        data_mode: DataMode,
535        cmd: Command,
536        address: Address,
537        dummy: u8,
538        buffer: &mut [u8],
539    ) -> Result<(), Error> {
540        if buffer.is_empty() {
541            error!("Half-duplex mode does not support empty buffer");
542            return Err(Error::Unsupported);
543        }
544
545        self.setup_half_duplex(
546            false,
547            cmd,
548            address,
549            false,
550            dummy,
551            buffer.is_empty(),
552            data_mode,
553        )?;
554
555        let _keep_cs_guard = DropGuard::new((), |_| version::set_cs_keep_active(self, false));
556        let mut first = true;
557        let mut chunks = buffer.chunks_mut(FIFO_SIZE).peekable();
558        while let Some(chunk) = chunks.next() {
559            let last = chunks.peek().is_none();
560            self.prepare_half_duplex_chunk(first, last);
561            self.configure_datalen(chunk.len(), 0);
562            self.start_operation();
563            self.flush()?;
564            self.read_from_fifo(chunk)?;
565            first = false;
566        }
567        Ok(())
568    }
569
570    /// Blocking, FIFO-based half-duplex write.
571    ///
572    /// Performs the command, address, dummy, and data phases as a single SPI transaction without
573    /// involving the DMA engine. Transfers larger than the FIFO are split into chunks while keeping
574    /// CS asserted.
575    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
576    pub(super) fn half_duplex_write(
577        &self,
578        data_mode: DataMode,
579        cmd: Command,
580        address: Address,
581        dummy: u8,
582        buffer: &[u8],
583    ) -> Result<(), Error> {
584        cfg_select! {
585            all(spi_master_version = "1", spi_address_workaround) => {
586                let mut buffer = buffer;
587                let mut data_mode = data_mode;
588                let mut address = address;
589                let addr_bytes;
590                if buffer.is_empty() && !address.is_none() {
591                    // If the buffer is empty, we need to send a dummy byte
592                    // to trigger the address phase.
593                    let bytes_to_write = address.width().div_ceil(8);
594                    // The address register is read in big-endian order,
595                    // we have to prepare the emulated write in the same way.
596                    addr_bytes = address.value().to_be_bytes();
597                    buffer = &addr_bytes[4 - bytes_to_write..][..bytes_to_write];
598                    data_mode = address.mode();
599                    address = Address::None;
600                }
601
602                if dummy > 0 {
603                    // FIXME: https://github.com/esp-rs/esp-hal/issues/2240
604                    error!("Dummy bits are not supported without data");
605                    return Err(Error::Unsupported);
606                }
607            }
608            _ => {}
609        }
610
611        self.setup_half_duplex(
612            true,
613            cmd,
614            address,
615            false,
616            dummy,
617            buffer.is_empty(),
618            data_mode,
619        )?;
620
621        let _keep_cs_guard = DropGuard::new((), |_| version::set_cs_keep_active(self, false));
622        if buffer.is_empty() {
623            self.prepare_half_duplex_chunk(true, true);
624            self.start_operation();
625            self.flush()?;
626        } else {
627            let mut first = true;
628            let mut chunks = buffer.chunks(FIFO_SIZE).peekable();
629            while let Some(chunk) = chunks.next() {
630                let last = chunks.peek().is_none();
631                self.prepare_half_duplex_chunk(first, last);
632                self.configure_datalen(0, chunk.len());
633                self.fill_fifo(chunk);
634                self.start_operation();
635                self.flush()?;
636                first = false;
637            }
638        }
639        Ok(())
640    }
641
642    /// Asynchronous, FIFO-based half-duplex read.
643    ///
644    /// Performs the command, address, dummy, and data phases as a single SPI transaction without
645    /// involving the DMA engine. Transfers larger than the FIFO are split into chunks while keeping
646    /// CS asserted.
647    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
648    pub(super) async fn half_duplex_read_async(
649        &self,
650        data_mode: DataMode,
651        cmd: Command,
652        address: Address,
653        dummy: u8,
654        buffer: &mut [u8],
655    ) -> Result<(), Error> {
656        if buffer.is_empty() {
657            error!("Half-duplex mode does not support empty buffer");
658            return Err(Error::Unsupported);
659        }
660
661        self.setup_half_duplex(
662            false,
663            cmd,
664            address,
665            false,
666            dummy,
667            buffer.is_empty(),
668            data_mode,
669        )?;
670
671        let _keep_cs_guard = DropGuard::new((), |_| version::set_cs_keep_active(self, false));
672        let mut first = true;
673        let mut chunks = buffer.chunks_mut(FIFO_SIZE).peekable();
674        while let Some(chunk) = chunks.next() {
675            let last = chunks.peek().is_none();
676            self.prepare_half_duplex_chunk(first, last);
677            self.configure_datalen(chunk.len(), 0);
678            self.start_operation();
679
680            let cancel_on_drop = DropGuard::new((), |_| {
681                self.abort_transfer();
682                let _ = self.flush();
683            });
684            self.flush_async().await;
685            cancel_on_drop.defuse();
686
687            self.read_from_fifo(chunk)?;
688            first = false;
689        }
690        Ok(())
691    }
692
693    /// Asynchronous, FIFO-based half-duplex write.
694    ///
695    /// Performs the command, address, dummy, and data phases as a single SPI transaction without
696    /// involving the DMA engine. Transfers larger than the FIFO are split into chunks while keeping
697    /// CS asserted.
698    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
699    pub(super) async fn half_duplex_write_async(
700        &self,
701        data_mode: DataMode,
702        cmd: Command,
703        address: Address,
704        dummy: u8,
705        buffer: &[u8],
706    ) -> Result<(), Error> {
707        cfg_select! {
708            all(spi_master_version = "1", spi_address_workaround) => {
709                let mut buffer = buffer;
710                let mut data_mode = data_mode;
711                let mut address = address;
712                let addr_bytes;
713                if buffer.is_empty() && !address.is_none() {
714                    // If the buffer is empty, we need to send a dummy byte
715                    // to trigger the address phase.
716                    let bytes_to_write = address.width().div_ceil(8);
717                    // The address register is read in big-endian order,
718                    // we have to prepare the emulated write in the same way.
719                    addr_bytes = address.value().to_be_bytes();
720                    buffer = &addr_bytes[4 - bytes_to_write..][..bytes_to_write];
721                    data_mode = address.mode();
722                    address = Address::None;
723                }
724
725                if dummy > 0 {
726                    // FIXME: https://github.com/esp-rs/esp-hal/issues/2240
727                    error!("Dummy bits are not supported without data");
728                    return Err(Error::Unsupported);
729                }
730            }
731            _ => {}
732        }
733
734        self.setup_half_duplex(
735            true,
736            cmd,
737            address,
738            false,
739            dummy,
740            buffer.is_empty(),
741            data_mode,
742        )?;
743
744        let _keep_cs_guard = DropGuard::new((), |_| version::set_cs_keep_active(self, false));
745        if buffer.is_empty() {
746            self.prepare_half_duplex_chunk(true, true);
747            self.start_operation();
748
749            let cancel_on_drop = DropGuard::new((), |_| {
750                self.abort_transfer();
751                let _ = self.flush();
752            });
753            self.flush_async().await;
754            cancel_on_drop.defuse();
755        } else {
756            let mut first = true;
757            let mut chunks = buffer.chunks(FIFO_SIZE).peekable();
758            while let Some(chunk) = chunks.next() {
759                let last = chunks.peek().is_none();
760                self.prepare_half_duplex_chunk(first, last);
761                self.configure_datalen(0, chunk.len());
762                self.fill_fifo(chunk);
763                self.start_operation();
764
765                let cancel_on_drop = DropGuard::new((), |_| {
766                    self.abort_transfer();
767                    let _ = self.flush();
768                });
769                self.flush_async().await;
770                cancel_on_drop.defuse();
771
772                first = false;
773            }
774        }
775        Ok(())
776    }
777
778    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
779    pub(super) async fn transfer_in_place_async(&self, words: &mut [u8]) -> Result<(), Error> {
780        for chunk in words.chunks_mut(FIFO_SIZE) {
781            // Cut the transfer short if the future is dropped. We'll block for a short
782            // while to ensure the peripheral is idle.
783            let cancel_on_drop = DropGuard::new((), |_| {
784                self.abort_transfer();
785                let _ = self.flush();
786            });
787            let res = self.write_one(chunk);
788            self.flush_async().await;
789            cancel_on_drop.defuse();
790            res?;
791
792            self.read_from_fifo(chunk)?;
793        }
794
795        Ok(())
796    }
797
798    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
799    pub(super) async fn transfer_async(&self, read: &mut [u8], write: &[u8]) -> Result<(), Error> {
800        let mut write_from = 0;
801        let mut read_from = 0;
802
803        loop {
804            // How many bytes we write in this chunk
805            let write_inc = core::cmp::min(FIFO_SIZE, write.len() - write_from);
806            // How many bytes we read in this chunk
807            let read_inc = core::cmp::min(FIFO_SIZE, read.len() - read_from);
808
809            if (write_inc == 0) && (read_inc == 0) {
810                break;
811            }
812
813            self.flush_async().await;
814
815            if write_inc < read_inc {
816                // Read more than we write, must pad writing part with zeros
817                let mut empty = [EMPTY_WRITE_PAD; FIFO_SIZE];
818                empty[0..write_inc].copy_from_slice(&write[write_from..][..write_inc]);
819                self.write_one(&empty[..read_inc])?;
820            } else {
821                self.write_one(&write[write_from..][..write_inc])?;
822            }
823
824            self.flush_async().await;
825
826            if read_inc > 0 {
827                self.read_from_fifo(&mut read[read_from..][..read_inc])?;
828            }
829
830            write_from += write_inc;
831            read_from += read_inc;
832        }
833        Ok(())
834    }
835
836    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
837    pub(super) fn start_operation(&self) {
838        self.update();
839        self.clear_interrupts(SpiInterrupt::TransferDone.into());
840        self.regs().cmd().modify(|_, w| w.usr().set_bit());
841    }
842
843    pub(super) fn setup_full_duplex(&self) -> Result<(), Error> {
844        self.regs().user().modify(|_, w| {
845            w.usr_miso().set_bit();
846            w.usr_mosi().set_bit();
847            w.doutdin().set_bit();
848            w.usr_dummy().clear_bit();
849            w.sio().clear_bit()
850        });
851
852        self.init_spi_data_mode(
853            DataMode::SingleTwoDataLines,
854            DataMode::SingleTwoDataLines,
855            DataMode::SingleTwoDataLines,
856        )?;
857
858        version::setup_full_duplex(self);
859
860        Ok(())
861    }
862
863    #[expect(clippy::too_many_arguments)]
864    pub(super) fn setup_half_duplex(
865        &self,
866        is_write: bool,
867        cmd: Command,
868        address: Address,
869        dummy_idle: bool,
870        dummy: u8,
871        no_mosi_miso: bool,
872        data_mode: DataMode,
873    ) -> Result<(), Error> {
874        self.init_spi_data_mode(cmd.mode(), address.mode(), data_mode)?;
875
876        let dummy = version::prepare_half_duplex(self, is_write, dummy);
877
878        let reg_block = self.regs();
879        reg_block.user().modify(|_, w| {
880            w.usr_miso_highpart().clear_bit();
881            w.usr_mosi_highpart().clear_bit();
882            // This bit tells the hardware whether we use Single or SingleTwoDataLines
883            w.sio().bit(data_mode == DataMode::Single);
884            w.doutdin().clear_bit();
885            w.usr_miso().bit(!is_write && !no_mosi_miso);
886            w.usr_mosi().bit(is_write && !no_mosi_miso);
887            w.cs_hold().set_bit();
888            w.usr_dummy_idle().bit(dummy_idle);
889            w.usr_dummy().bit(dummy != 0);
890            w.usr_addr().bit(!address.is_none());
891            w.usr_command().bit(!cmd.is_none())
892        });
893
894        version::setup_half_duplex(self);
895
896        reg_block.slave().write(|w| unsafe { w.bits(0) });
897
898        self.update();
899
900        // set cmd, address, dummy cycles
901        self.set_up_common_phases(cmd, address, dummy);
902
903        Ok(())
904    }
905
906    pub(super) fn set_up_common_phases(&self, cmd: Command, address: Address, dummy: u8) {
907        let reg_block = self.regs();
908        if !cmd.is_none() {
909            reg_block.user2().modify(|_, w| unsafe {
910                w.usr_command_bitlen().bits((cmd.width() - 1) as u8);
911                w.usr_command_value().bits(cmd.value())
912            });
913        }
914
915        if !address.is_none() {
916            reg_block
917                .user1()
918                .modify(|_, w| unsafe { w.usr_addr_bitlen().bits((address.width() - 1) as u8) });
919
920            version::write_address(self, address.value() << (32 - address.width()));
921        }
922
923        if dummy > 0 {
924            reg_block
925                .user1()
926                .modify(|_, w| unsafe { w.usr_dummy_cyclelen().bits(dummy - 1) });
927        }
928    }
929
930    pub(super) fn update(&self) {
931        cfg_select! {
932            spi_master_version = "3" => {
933                let reg_block = self.regs();
934
935                reg_block.cmd().modify(|_, w| w.update().set_bit());
936
937                while reg_block.cmd().read().update().bit_is_set() {
938                    // wait
939                }
940            }
941            _ => {
942                // Doesn't seem to be needed for ESP32 and ESP32-S2
943            }
944        }
945    }
946
947    pub(super) fn configure_datalen(&self, rx_len_bytes: usize, tx_len_bytes: usize) {
948        let rx_len = rx_len_bytes as u32 * 8;
949        let tx_len = tx_len_bytes as u32 * 8;
950
951        version::configure_datalen(self, rx_len.saturating_sub(1), tx_len.saturating_sub(1));
952    }
953}
954
955impl PartialEq for Info {
956    fn eq(&self, other: &Self) -> bool {
957        core::ptr::eq(self.register_block, other.register_block)
958    }
959}
960
961unsafe impl Sync for Info {}
962
963for_each_spi_master! {
964    ($peri:ident, $sys:ident, $sclk:ident [$($cs:ident),+] [$($sio:ident),*] $(, $is_qspi:tt)?) => {
965        impl Instance for crate::peripherals::$peri<'_> {
966            #[inline(always)]
967            fn parts(&self) -> (&'static Info, &'static State) {
968                #[handler]
969                #[ram]
970                fn irq_handler() {
971                    handle_async(&INFO, &STATE)
972                }
973
974                static INFO: Info = Info {
975                    register_block: crate::peripherals::$peri::ptr(),
976                    peripheral: crate::system::Peripheral::$sys,
977                    async_handler: irq_handler,
978                    sclk: OutputSignal::$sclk,
979                    cs: &[$(OutputSignal::$cs),+],
980                    sio_inputs: &[$(InputSignal::$sio),*],
981                    sio_outputs: &[$(OutputSignal::$sio),*],
982                    clock_instance: crate::soc::clocks::SpiInstance::$sys,
983                };
984
985                static STATE: State = State {
986                    waker: AtomicWaker::new(),
987                    pins: UnsafeCell::new(MaybeUninit::uninit()),
988                    min_async_transfer_size: AtomicUsize::new(0),
989
990                    #[cfg(spi_master_version = "1")]
991                    esp32_hack: Esp32Hack {
992                        timing_miso_delay: Cell::new(None),
993                        extra_dummy: Cell::new(0),
994                    },
995                };
996
997                (&INFO, &STATE)
998            }
999        }
1000
1001        $(
1002            // If the extra pins are set, implement QspiInstance
1003            $crate::ignore!($is_qspi);
1004            impl QspiInstance for crate::peripherals::$peri<'_> {}
1005        )?
1006    };
1007}
1008
1009#[doc(hidden)]
1010pub struct State {
1011    pub(super) waker: AtomicWaker,
1012    pins: UnsafeCell<MaybeUninit<SpiPinGuard>>,
1013    pub(super) min_async_transfer_size: AtomicUsize,
1014
1015    #[cfg(spi_master_version = "1")]
1016    esp32_hack: Esp32Hack,
1017}
1018
1019impl State {
1020    // Syntactic helper to get a mutable reference to the pin guard.
1021    //
1022    // Intended to be called in `SpiWrapper::pins` only
1023    //
1024    // # Safety
1025    //
1026    // The caller must ensure that Rust's aliasing rules are upheld.
1027    #[allow(
1028        clippy::mut_from_ref,
1029        reason = "Safety requirements ensure this is okay"
1030    )]
1031    pub(super) unsafe fn pins(&self) -> &mut SpiPinGuard {
1032        unsafe { (&mut *self.pins.get()).assume_init_mut() }
1033    }
1034
1035    unsafe fn deinit(&self) {
1036        unsafe {
1037            let mut old = self.pins.get().replace(MaybeUninit::uninit());
1038            old.assume_init_drop();
1039        }
1040    }
1041}
1042
1043#[cfg(spi_master_version = "1")]
1044pub(super) struct Esp32Hack {
1045    timing_miso_delay: Cell<Option<u8>>,
1046    extra_dummy: Cell<u8>,
1047}
1048
1049unsafe impl Sync for State {}
1050
1051#[ram]
1052pub(super) fn handle_async(info: &'static Info, state: &'static State) {
1053    let driver = Driver { info, state };
1054    if driver.interrupts().contains(SpiInterrupt::TransferDone) {
1055        driver.enable_listen(SpiInterrupt::TransferDone.into(), false);
1056        state.waker.wake();
1057    }
1058}
1059
1060#[must_use = "futures do nothing unless you `.await` or poll them"]
1061struct SpiFuture<'a> {
1062    driver: &'a Driver,
1063}
1064
1065impl SpiFuture<'_> {
1066    const EVENTS: EnumSet<SpiInterrupt> = enum_set!(SpiInterrupt::TransferDone);
1067}
1068
1069impl Future for SpiFuture<'_> {
1070    type Output = ();
1071
1072    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
1073    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
1074        if !self.driver.busy() {
1075            self.driver.clear_interrupts(Self::EVENTS);
1076            return Poll::Ready(());
1077        }
1078
1079        self.driver.state.waker.register(cx.waker());
1080        self.driver.enable_listen(Self::EVENTS, true);
1081
1082        // On some chips the interrupt enable bit and the interrupt status bit are in the same
1083        // register. If the transfer ends while we enable the interrupt, the read-modify-write
1084        // clears the status bit, and the peripheral does not request an interrupt. Check the
1085        // peripheral again to detect this case.
1086        if self.driver.busy() {
1087            Poll::Pending
1088        } else {
1089            self.driver.clear_interrupts(Self::EVENTS);
1090            Poll::Ready(())
1091        }
1092    }
1093}
1094
1095impl Drop for SpiFuture<'_> {
1096    fn drop(&mut self) {
1097        self.driver.enable_listen(Self::EVENTS, false);
1098    }
1099}