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    /// Use [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    /// Clock 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    /// Initialize 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    /// Enable or disable 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    /// Gets 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    /// Write 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    /// Write 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    /// Write 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    /// Read bytes from SPI.
393    ///
394    /// Sends out a stuffing byte for every byte to read. This function doesn't
395    /// perform flushing. If you want to read the response to something you
396    /// have written before, consider using [`Self::transfer`] 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    /// Read bytes from SPI.
410    ///
411    /// Sends out a stuffing byte for every byte to read. If you want to read
412    /// the response to something you have written before, consider using
413    /// [`Self::transfer`] instead.
414    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
415    pub(super) async fn read_async(&self, words: &mut [u8]) -> Result<(), Error> {
416        let empty_array = [EMPTY_WRITE_PAD; FIFO_SIZE];
417
418        for chunk in words.chunks_mut(FIFO_SIZE) {
419            self.write_one(&empty_array[0..chunk.len()])?;
420            self.flush_async().await;
421            self.read_from_fifo(chunk)?;
422        }
423        Ok(())
424    }
425
426    /// Read received bytes from SPI FIFO.
427    ///
428    /// Copies the contents of the SPI receive FIFO into `words`. This function
429    /// doesn't perform any data transfer. If you want to read the response to
430    /// something you have written before, consider using [`Self::transfer`]
431    /// instead.
432    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
433    pub(super) fn read_from_fifo(&self, words: &mut [u8]) -> Result<(), Error> {
434        if words.len() > FIFO_SIZE {
435            return Err(Error::FifoSizeExeeded);
436        }
437
438        for (chunk, w_reg) in words.chunks_mut(4).zip(self.regs().w_iter()) {
439            let reg_val = w_reg.read().bits();
440            let bytes = reg_val.to_le_bytes();
441
442            let len = chunk.len();
443            chunk.copy_from_slice(&bytes[..len]);
444        }
445
446        Ok(())
447    }
448
449    pub(super) fn busy(&self) -> bool {
450        self.regs().cmd().read().usr().bit_is_set()
451    }
452
453    // Check if the bus is busy and if it is wait for it to be idle
454    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
455    pub(super) fn flush_async(&self) -> impl Future<Output = ()> {
456        SpiFuture { driver: self }
457    }
458
459    // Check if the bus is busy and if it is wait for it to be idle
460    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
461    pub(super) fn flush(&self) -> Result<(), Error> {
462        while self.busy() {
463            // wait for bus to be clear
464        }
465        Ok(())
466    }
467
468    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
469    pub(super) fn transfer_in_place(&self, words: &mut [u8]) -> Result<(), Error> {
470        for chunk in words.chunks_mut(FIFO_SIZE) {
471            self.write_one(chunk)?;
472            self.flush()?;
473            self.read_from_fifo(chunk)?;
474        }
475
476        Ok(())
477    }
478
479    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
480    pub(super) fn transfer(&self, read: &mut [u8], write: &[u8]) -> Result<(), Error> {
481        let mut write_from = 0;
482        let mut read_from = 0;
483
484        loop {
485            // How many bytes we write in this chunk
486            let write_inc = core::cmp::min(FIFO_SIZE, write.len() - write_from);
487            // How many bytes we read in this chunk
488            let read_inc = core::cmp::min(FIFO_SIZE, read.len() - read_from);
489
490            if (write_inc == 0) && (read_inc == 0) {
491                break;
492            }
493
494            if write_inc < read_inc {
495                // Read more than we write, must pad writing part with zeros
496                let mut empty = [EMPTY_WRITE_PAD; FIFO_SIZE];
497                empty[0..write_inc].copy_from_slice(&write[write_from..][..write_inc]);
498                self.write_one(&empty[..read_inc])?;
499            } else {
500                self.write_one(&write[write_from..][..write_inc])?;
501            }
502
503            self.flush()?;
504
505            if read_inc > 0 {
506                self.read_from_fifo(&mut read[read_from..][..read_inc])?;
507            }
508
509            write_from += write_inc;
510            read_from += read_inc;
511        }
512        Ok(())
513    }
514
515    fn prepare_half_duplex_chunk(&self, first: bool, last: bool) {
516        self.regs().user().modify(|_, w| {
517            if !first {
518                w.usr_command().clear_bit();
519                w.usr_addr().clear_bit();
520                w.usr_dummy().clear_bit();
521                w.cs_setup().clear_bit();
522            }
523            w.cs_hold().bit(!last)
524        });
525        version::set_cs_keep_active(self, !last);
526    }
527
528    /// Blocking, FIFO-based half-duplex read.
529    ///
530    /// Performs the command, address, dummy, and data phases as a single SPI transaction without
531    /// involving the DMA engine. Transfers larger than the FIFO are split into chunks while keeping
532    /// CS asserted.
533    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
534    pub(super) fn half_duplex_read(
535        &self,
536        data_mode: DataMode,
537        cmd: Command,
538        address: Address,
539        dummy: u8,
540        buffer: &mut [u8],
541    ) -> Result<(), Error> {
542        if buffer.is_empty() {
543            error!("Half-duplex mode does not support empty buffer");
544            return Err(Error::Unsupported);
545        }
546
547        self.setup_half_duplex(
548            false,
549            cmd,
550            address,
551            false,
552            dummy,
553            buffer.is_empty(),
554            data_mode,
555        )?;
556
557        let _keep_cs_guard = DropGuard::new((), |_| version::set_cs_keep_active(self, false));
558        let mut first = true;
559        let mut chunks = buffer.chunks_mut(FIFO_SIZE).peekable();
560        while let Some(chunk) = chunks.next() {
561            let last = chunks.peek().is_none();
562            self.prepare_half_duplex_chunk(first, last);
563            self.configure_datalen(chunk.len(), 0);
564            self.start_operation();
565            self.flush()?;
566            self.read_from_fifo(chunk)?;
567            first = false;
568        }
569        Ok(())
570    }
571
572    /// Blocking, FIFO-based half-duplex write.
573    ///
574    /// Performs the command, address, dummy, and data phases as a single SPI transaction without
575    /// involving the DMA engine. Transfers larger than the FIFO are split into chunks while keeping
576    /// CS asserted.
577    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
578    pub(super) fn half_duplex_write(
579        &self,
580        data_mode: DataMode,
581        cmd: Command,
582        address: Address,
583        dummy: u8,
584        buffer: &[u8],
585    ) -> Result<(), Error> {
586        cfg_select! {
587            all(spi_master_version = "1", spi_address_workaround) => {
588                let mut buffer = buffer;
589                let mut data_mode = data_mode;
590                let mut address = address;
591                let addr_bytes;
592                if buffer.is_empty() && !address.is_none() {
593                    // If the buffer is empty, we need to send a dummy byte
594                    // to trigger the address phase.
595                    let bytes_to_write = address.width().div_ceil(8);
596                    // The address register is read in big-endian order,
597                    // we have to prepare the emulated write in the same way.
598                    addr_bytes = address.value().to_be_bytes();
599                    buffer = &addr_bytes[4 - bytes_to_write..][..bytes_to_write];
600                    data_mode = address.mode();
601                    address = Address::None;
602                }
603
604                if dummy > 0 {
605                    // FIXME: https://github.com/esp-rs/esp-hal/issues/2240
606                    error!("Dummy bits are not supported without data");
607                    return Err(Error::Unsupported);
608                }
609            }
610            _ => {}
611        }
612
613        self.setup_half_duplex(
614            true,
615            cmd,
616            address,
617            false,
618            dummy,
619            buffer.is_empty(),
620            data_mode,
621        )?;
622
623        let _keep_cs_guard = DropGuard::new((), |_| version::set_cs_keep_active(self, false));
624        if buffer.is_empty() {
625            self.prepare_half_duplex_chunk(true, true);
626            self.start_operation();
627            self.flush()?;
628        } else {
629            let mut first = true;
630            let mut chunks = buffer.chunks(FIFO_SIZE).peekable();
631            while let Some(chunk) = chunks.next() {
632                let last = chunks.peek().is_none();
633                self.prepare_half_duplex_chunk(first, last);
634                self.configure_datalen(0, chunk.len());
635                self.fill_fifo(chunk);
636                self.start_operation();
637                self.flush()?;
638                first = false;
639            }
640        }
641        Ok(())
642    }
643
644    /// Asynchronous, FIFO-based half-duplex read.
645    ///
646    /// Performs the command, address, dummy, and data phases as a single SPI transaction without
647    /// involving the DMA engine. Transfers larger than the FIFO are split into chunks while keeping
648    /// CS asserted.
649    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
650    pub(super) async fn half_duplex_read_async(
651        &self,
652        data_mode: DataMode,
653        cmd: Command,
654        address: Address,
655        dummy: u8,
656        buffer: &mut [u8],
657    ) -> Result<(), Error> {
658        if buffer.is_empty() {
659            error!("Half-duplex mode does not support empty buffer");
660            return Err(Error::Unsupported);
661        }
662
663        self.setup_half_duplex(
664            false,
665            cmd,
666            address,
667            false,
668            dummy,
669            buffer.is_empty(),
670            data_mode,
671        )?;
672
673        let _keep_cs_guard = DropGuard::new((), |_| version::set_cs_keep_active(self, false));
674        let mut first = true;
675        let mut chunks = buffer.chunks_mut(FIFO_SIZE).peekable();
676        while let Some(chunk) = chunks.next() {
677            let last = chunks.peek().is_none();
678            self.prepare_half_duplex_chunk(first, last);
679            self.configure_datalen(chunk.len(), 0);
680            self.start_operation();
681
682            let cancel_on_drop = DropGuard::new((), |_| {
683                self.abort_transfer();
684                let _ = self.flush();
685            });
686            self.flush_async().await;
687            cancel_on_drop.defuse();
688
689            self.read_from_fifo(chunk)?;
690            first = false;
691        }
692        Ok(())
693    }
694
695    /// Asynchronous, FIFO-based half-duplex write.
696    ///
697    /// Performs the command, address, dummy, and data phases as a single SPI transaction without
698    /// involving the DMA engine. Transfers larger than the FIFO are split into chunks while keeping
699    /// CS asserted.
700    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
701    pub(super) async fn half_duplex_write_async(
702        &self,
703        data_mode: DataMode,
704        cmd: Command,
705        address: Address,
706        dummy: u8,
707        buffer: &[u8],
708    ) -> Result<(), Error> {
709        cfg_select! {
710            all(spi_master_version = "1", spi_address_workaround) => {
711                let mut buffer = buffer;
712                let mut data_mode = data_mode;
713                let mut address = address;
714                let addr_bytes;
715                if buffer.is_empty() && !address.is_none() {
716                    // If the buffer is empty, we need to send a dummy byte
717                    // to trigger the address phase.
718                    let bytes_to_write = address.width().div_ceil(8);
719                    // The address register is read in big-endian order,
720                    // we have to prepare the emulated write in the same way.
721                    addr_bytes = address.value().to_be_bytes();
722                    buffer = &addr_bytes[4 - bytes_to_write..][..bytes_to_write];
723                    data_mode = address.mode();
724                    address = Address::None;
725                }
726
727                if dummy > 0 {
728                    // FIXME: https://github.com/esp-rs/esp-hal/issues/2240
729                    error!("Dummy bits are not supported without data");
730                    return Err(Error::Unsupported);
731                }
732            }
733            _ => {}
734        }
735
736        self.setup_half_duplex(
737            true,
738            cmd,
739            address,
740            false,
741            dummy,
742            buffer.is_empty(),
743            data_mode,
744        )?;
745
746        let _keep_cs_guard = DropGuard::new((), |_| version::set_cs_keep_active(self, false));
747        if buffer.is_empty() {
748            self.prepare_half_duplex_chunk(true, true);
749            self.start_operation();
750
751            let cancel_on_drop = DropGuard::new((), |_| {
752                self.abort_transfer();
753                let _ = self.flush();
754            });
755            self.flush_async().await;
756            cancel_on_drop.defuse();
757        } else {
758            let mut first = true;
759            let mut chunks = buffer.chunks(FIFO_SIZE).peekable();
760            while let Some(chunk) = chunks.next() {
761                let last = chunks.peek().is_none();
762                self.prepare_half_duplex_chunk(first, last);
763                self.configure_datalen(0, chunk.len());
764                self.fill_fifo(chunk);
765                self.start_operation();
766
767                let cancel_on_drop = DropGuard::new((), |_| {
768                    self.abort_transfer();
769                    let _ = self.flush();
770                });
771                self.flush_async().await;
772                cancel_on_drop.defuse();
773
774                first = false;
775            }
776        }
777        Ok(())
778    }
779
780    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
781    pub(super) async fn transfer_in_place_async(&self, words: &mut [u8]) -> Result<(), Error> {
782        for chunk in words.chunks_mut(FIFO_SIZE) {
783            // Cut the transfer short if the future is dropped. We'll block for a short
784            // while to ensure the peripheral is idle.
785            let cancel_on_drop = DropGuard::new((), |_| {
786                self.abort_transfer();
787                let _ = self.flush();
788            });
789            let res = self.write_one(chunk);
790            self.flush_async().await;
791            cancel_on_drop.defuse();
792            res?;
793
794            self.read_from_fifo(chunk)?;
795        }
796
797        Ok(())
798    }
799
800    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
801    pub(super) async fn transfer_async(&self, read: &mut [u8], write: &[u8]) -> Result<(), Error> {
802        let mut write_from = 0;
803        let mut read_from = 0;
804
805        loop {
806            // How many bytes we write in this chunk
807            let write_inc = core::cmp::min(FIFO_SIZE, write.len() - write_from);
808            // How many bytes we read in this chunk
809            let read_inc = core::cmp::min(FIFO_SIZE, read.len() - read_from);
810
811            if (write_inc == 0) && (read_inc == 0) {
812                break;
813            }
814
815            self.flush_async().await;
816
817            if write_inc < read_inc {
818                // Read more than we write, must pad writing part with zeros
819                let mut empty = [EMPTY_WRITE_PAD; FIFO_SIZE];
820                empty[0..write_inc].copy_from_slice(&write[write_from..][..write_inc]);
821                self.write_one(&empty[..read_inc])?;
822            } else {
823                self.write_one(&write[write_from..][..write_inc])?;
824            }
825
826            self.flush_async().await;
827
828            if read_inc > 0 {
829                self.read_from_fifo(&mut read[read_from..][..read_inc])?;
830            }
831
832            write_from += write_inc;
833            read_from += read_inc;
834        }
835        Ok(())
836    }
837
838    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
839    pub(super) fn start_operation(&self) {
840        self.update();
841        self.clear_interrupts(SpiInterrupt::TransferDone.into());
842        self.regs().cmd().modify(|_, w| w.usr().set_bit());
843    }
844
845    pub(super) fn setup_full_duplex(&self) -> Result<(), Error> {
846        self.regs().user().modify(|_, w| {
847            w.usr_miso().set_bit();
848            w.usr_mosi().set_bit();
849            w.doutdin().set_bit();
850            w.usr_dummy().clear_bit();
851            w.sio().clear_bit()
852        });
853
854        self.init_spi_data_mode(
855            DataMode::SingleTwoDataLines,
856            DataMode::SingleTwoDataLines,
857            DataMode::SingleTwoDataLines,
858        )?;
859
860        version::setup_full_duplex(self);
861
862        Ok(())
863    }
864
865    #[expect(clippy::too_many_arguments)]
866    pub(super) fn setup_half_duplex(
867        &self,
868        is_write: bool,
869        cmd: Command,
870        address: Address,
871        dummy_idle: bool,
872        dummy: u8,
873        no_mosi_miso: bool,
874        data_mode: DataMode,
875    ) -> Result<(), Error> {
876        self.init_spi_data_mode(cmd.mode(), address.mode(), data_mode)?;
877
878        let dummy = version::prepare_half_duplex(self, is_write, dummy);
879
880        let reg_block = self.regs();
881        reg_block.user().modify(|_, w| {
882            w.usr_miso_highpart().clear_bit();
883            w.usr_mosi_highpart().clear_bit();
884            // This bit tells the hardware whether we use Single or SingleTwoDataLines
885            w.sio().bit(data_mode == DataMode::Single);
886            w.doutdin().clear_bit();
887            w.usr_miso().bit(!is_write && !no_mosi_miso);
888            w.usr_mosi().bit(is_write && !no_mosi_miso);
889            w.cs_hold().set_bit();
890            w.usr_dummy_idle().bit(dummy_idle);
891            w.usr_dummy().bit(dummy != 0);
892            w.usr_addr().bit(!address.is_none());
893            w.usr_command().bit(!cmd.is_none())
894        });
895
896        version::setup_half_duplex(self);
897
898        reg_block.slave().write(|w| unsafe { w.bits(0) });
899
900        self.update();
901
902        // set cmd, address, dummy cycles
903        self.set_up_common_phases(cmd, address, dummy);
904
905        Ok(())
906    }
907
908    pub(super) fn set_up_common_phases(&self, cmd: Command, address: Address, dummy: u8) {
909        let reg_block = self.regs();
910        if !cmd.is_none() {
911            reg_block.user2().modify(|_, w| unsafe {
912                w.usr_command_bitlen().bits((cmd.width() - 1) as u8);
913                w.usr_command_value().bits(cmd.value())
914            });
915        }
916
917        if !address.is_none() {
918            reg_block
919                .user1()
920                .modify(|_, w| unsafe { w.usr_addr_bitlen().bits((address.width() - 1) as u8) });
921
922            version::write_address(self, address.value() << (32 - address.width()));
923        }
924
925        if dummy > 0 {
926            reg_block
927                .user1()
928                .modify(|_, w| unsafe { w.usr_dummy_cyclelen().bits(dummy - 1) });
929        }
930    }
931
932    pub(super) fn update(&self) {
933        cfg_select! {
934            spi_master_version = "3" => {
935                let reg_block = self.regs();
936
937                reg_block.cmd().modify(|_, w| w.update().set_bit());
938
939                while reg_block.cmd().read().update().bit_is_set() {
940                    // wait
941                }
942            }
943            _ => {
944                // Doesn't seem to be needed for ESP32 and ESP32-S2
945            }
946        }
947    }
948
949    pub(super) fn configure_datalen(&self, rx_len_bytes: usize, tx_len_bytes: usize) {
950        let rx_len = rx_len_bytes as u32 * 8;
951        let tx_len = tx_len_bytes as u32 * 8;
952
953        version::configure_datalen(self, rx_len.saturating_sub(1), tx_len.saturating_sub(1));
954    }
955}
956
957impl PartialEq for Info {
958    fn eq(&self, other: &Self) -> bool {
959        core::ptr::eq(self.register_block, other.register_block)
960    }
961}
962
963unsafe impl Sync for Info {}
964
965for_each_spi_master! {
966    ($peri:ident, $sys:ident, $sclk:ident [$($cs:ident),+] [$($sio:ident),*] $(, $is_qspi:tt)?) => {
967        impl Instance for crate::peripherals::$peri<'_> {
968            #[inline(always)]
969            fn parts(&self) -> (&'static Info, &'static State) {
970                #[handler]
971                #[ram]
972                fn irq_handler() {
973                    handle_async(&INFO, &STATE)
974                }
975
976                static INFO: Info = Info {
977                    register_block: crate::peripherals::$peri::ptr(),
978                    peripheral: crate::system::Peripheral::$sys,
979                    async_handler: irq_handler,
980                    sclk: OutputSignal::$sclk,
981                    cs: &[$(OutputSignal::$cs),+],
982                    sio_inputs: &[$(InputSignal::$sio),*],
983                    sio_outputs: &[$(OutputSignal::$sio),*],
984                    clock_instance: crate::soc::clocks::SpiInstance::$sys,
985                };
986
987                static STATE: State = State {
988                    waker: AtomicWaker::new(),
989                    pins: UnsafeCell::new(MaybeUninit::uninit()),
990                    min_async_transfer_size: AtomicUsize::new(0),
991
992                    #[cfg(spi_master_version = "1")]
993                    esp32_hack: Esp32Hack {
994                        timing_miso_delay: Cell::new(None),
995                        extra_dummy: Cell::new(0),
996                    },
997                };
998
999                (&INFO, &STATE)
1000            }
1001        }
1002
1003        $(
1004            // If the extra pins are set, implement QspiInstance
1005            $crate::ignore!($is_qspi);
1006            impl QspiInstance for crate::peripherals::$peri<'_> {}
1007        )?
1008    };
1009}
1010
1011#[doc(hidden)]
1012pub struct State {
1013    pub(super) waker: AtomicWaker,
1014    pins: UnsafeCell<MaybeUninit<SpiPinGuard>>,
1015    pub(super) min_async_transfer_size: AtomicUsize,
1016
1017    #[cfg(spi_master_version = "1")]
1018    esp32_hack: Esp32Hack,
1019}
1020
1021impl State {
1022    // Syntactic helper to get a mutable reference to the pin guard.
1023    //
1024    // Intended to be called in `SpiWrapper::pins` only
1025    //
1026    // # Safety
1027    //
1028    // The caller must ensure that Rust's aliasing rules are upheld.
1029    #[allow(
1030        clippy::mut_from_ref,
1031        reason = "Safety requirements ensure this is okay"
1032    )]
1033    pub(super) unsafe fn pins(&self) -> &mut SpiPinGuard {
1034        unsafe { (&mut *self.pins.get()).assume_init_mut() }
1035    }
1036
1037    unsafe fn deinit(&self) {
1038        unsafe {
1039            let mut old = self.pins.get().replace(MaybeUninit::uninit());
1040            old.assume_init_drop();
1041        }
1042    }
1043}
1044
1045#[cfg(spi_master_version = "1")]
1046pub(super) struct Esp32Hack {
1047    timing_miso_delay: Cell<Option<u8>>,
1048    extra_dummy: Cell<u8>,
1049}
1050
1051unsafe impl Sync for State {}
1052
1053#[ram]
1054pub(super) fn handle_async(info: &'static Info, state: &'static State) {
1055    let driver = Driver { info, state };
1056    if driver.interrupts().contains(SpiInterrupt::TransferDone) {
1057        driver.enable_listen(SpiInterrupt::TransferDone.into(), false);
1058        state.waker.wake();
1059    }
1060}
1061
1062#[must_use = "futures do nothing unless you `.await` or poll them"]
1063struct SpiFuture<'a> {
1064    driver: &'a Driver,
1065}
1066
1067impl SpiFuture<'_> {
1068    const EVENTS: EnumSet<SpiInterrupt> = enum_set!(SpiInterrupt::TransferDone);
1069}
1070
1071impl Future for SpiFuture<'_> {
1072    type Output = ();
1073
1074    #[cfg_attr(place_spi_master_driver_in_ram, ram)]
1075    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
1076        if !self.driver.busy() {
1077            self.driver.clear_interrupts(Self::EVENTS);
1078            return Poll::Ready(());
1079        }
1080
1081        self.driver.state.waker.register(cx.waker());
1082        self.driver.enable_listen(Self::EVENTS, true);
1083
1084        // On some chips the interrupt enable bit and the interrupt status bit are in the same
1085        // register. If the transfer ends while we enable the interrupt, the read-modify-write
1086        // clears the status bit, and the peripheral does not request an interrupt. Check the
1087        // peripheral again to detect this case.
1088        if self.driver.busy() {
1089            Poll::Pending
1090        } else {
1091            self.driver.clear_interrupts(Self::EVENTS);
1092            Poll::Ready(())
1093        }
1094    }
1095}
1096
1097impl Drop for SpiFuture<'_> {
1098    fn drop(&mut self) {
1099        self.driver.enable_listen(Self::EVENTS, false);
1100    }
1101}