zynq7000_hal/spi/
mod.rs

1//! SPI module
2//!
3//! ## Examples
4//!
5//! - [L3GD20H SPI sensor](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq/examples/zedboard/src/bin/l3gd20h-spi-mio.rs)
6use core::convert::Infallible;
7
8use crate::clocks::Clocks;
9use crate::gpio::IoPeriphPin;
10use crate::gpio::mio::{
11    Mio10, Mio11, Mio12, Mio13, Mio14, Mio15, Mio28, Mio29, Mio30, Mio31, Mio32, Mio33, Mio34,
12    Mio35, Mio36, Mio37, Mio38, Mio39, MioPin, MuxConfig, Pin,
13};
14#[cfg(not(feature = "7z010-7z007s-clg225"))]
15use crate::gpio::mio::{
16    Mio16, Mio17, Mio18, Mio19, Mio20, Mio21, Mio22, Mio23, Mio24, Mio25, Mio26, Mio27, Mio40,
17    Mio41, Mio42, Mio43, Mio44, Mio45, Mio46, Mio47, Mio48, Mio49, Mio50, Mio51,
18};
19use crate::{enable_amba_peripheral_clock, spi_mode_const_to_cpol_cpha};
20
21use crate::{clocks::IoClocks, slcr::Slcr, time::Hertz};
22use arbitrary_int::{prelude::*, u3, u4, u6};
23use embedded_hal::delay::DelayNs;
24pub use embedded_hal::spi::Mode;
25use embedded_hal::spi::SpiBus as _;
26use zynq7000::slcr::reset::DualRefAndClockReset;
27use zynq7000::spi::{
28    BaudDivSel, DelayControl, FifoWrite, InterruptControl, InterruptMask, InterruptStatus, MmioSpi,
29    SPI_0_BASE_ADDR, SPI_1_BASE_ADDR,
30};
31
32pub const FIFO_DEPTH: usize = 128;
33pub const MODULE_ID: u32 = 0x90106;
34
35pub mod asynch;
36pub use asynch::*;
37
38#[derive(Debug, Clone, Copy, PartialEq, Eq)]
39pub enum SpiId {
40    Spi0 = 0,
41    Spi1 = 1,
42}
43
44pub trait PsSpi {
45    fn reg_block(&self) -> MmioSpi<'static>;
46    fn id(&self) -> Option<SpiId>;
47}
48
49impl PsSpi for MmioSpi<'static> {
50    #[inline]
51    fn reg_block(&self) -> MmioSpi<'static> {
52        unsafe { self.clone() }
53    }
54
55    #[inline]
56    fn id(&self) -> Option<SpiId> {
57        let base_addr = unsafe { self.ptr() } as usize;
58        if base_addr == SPI_0_BASE_ADDR {
59            return Some(SpiId::Spi0);
60        } else if base_addr == SPI_1_BASE_ADDR {
61            return Some(SpiId::Spi1);
62        }
63        None
64    }
65}
66
67pub trait SckPin: MioPin {
68    const SPI: SpiId;
69    const GROUP: usize;
70}
71
72pub trait MosiPin: MioPin {
73    const SPI: SpiId;
74    const GROUP: usize;
75}
76
77pub trait MisoPin: MioPin {
78    const SPI: SpiId;
79    const GROUP: usize;
80}
81
82pub trait SsPin: MioPin {
83    const IDX: usize;
84    const SPI: SpiId;
85    const GROUP: usize;
86}
87
88pub const SPI_MUX_CONF: MuxConfig = MuxConfig::new_with_l3(u3::new(0b101));
89
90// SPI0, choice 1
91#[cfg(not(feature = "7z010-7z007s-clg225"))]
92impl SckPin for Pin<Mio16> {
93    const SPI: SpiId = SpiId::Spi0;
94    const GROUP: usize = 0;
95}
96#[cfg(not(feature = "7z010-7z007s-clg225"))]
97impl MosiPin for Pin<Mio21> {
98    const SPI: SpiId = SpiId::Spi0;
99    const GROUP: usize = 0;
100}
101#[cfg(not(feature = "7z010-7z007s-clg225"))]
102impl MisoPin for Pin<Mio17> {
103    const SPI: SpiId = SpiId::Spi0;
104    const GROUP: usize = 0;
105}
106#[cfg(not(feature = "7z010-7z007s-clg225"))]
107impl SsPin for Pin<Mio18> {
108    const SPI: SpiId = SpiId::Spi0;
109    const GROUP: usize = 0;
110    const IDX: usize = 0;
111}
112#[cfg(not(feature = "7z010-7z007s-clg225"))]
113impl SsPin for Pin<Mio19> {
114    const SPI: SpiId = SpiId::Spi0;
115    const GROUP: usize = 0;
116    const IDX: usize = 1;
117}
118#[cfg(not(feature = "7z010-7z007s-clg225"))]
119impl SsPin for Pin<Mio20> {
120    const SPI: SpiId = SpiId::Spi0;
121    const GROUP: usize = 0;
122    const IDX: usize = 2;
123}
124
125// SPI0, choice 2
126impl SckPin for Pin<Mio28> {
127    const SPI: SpiId = SpiId::Spi0;
128    const GROUP: usize = 1;
129}
130impl MosiPin for Pin<Mio33> {
131    const SPI: SpiId = SpiId::Spi0;
132    const GROUP: usize = 1;
133}
134impl MisoPin for Pin<Mio29> {
135    const SPI: SpiId = SpiId::Spi0;
136    const GROUP: usize = 1;
137}
138impl SsPin for Pin<Mio30> {
139    const SPI: SpiId = SpiId::Spi0;
140    const GROUP: usize = 1;
141    const IDX: usize = 0;
142}
143impl SsPin for Pin<Mio31> {
144    const SPI: SpiId = SpiId::Spi0;
145    const GROUP: usize = 1;
146    const IDX: usize = 1;
147}
148impl SsPin for Pin<Mio32> {
149    const SPI: SpiId = SpiId::Spi0;
150    const GROUP: usize = 1;
151    const IDX: usize = 2;
152}
153
154// SPI0, choice 3
155#[cfg(not(feature = "7z010-7z007s-clg225"))]
156impl SckPin for Pin<Mio40> {
157    const SPI: SpiId = SpiId::Spi0;
158    const GROUP: usize = 2;
159}
160#[cfg(not(feature = "7z010-7z007s-clg225"))]
161impl MosiPin for Pin<Mio45> {
162    const SPI: SpiId = SpiId::Spi0;
163    const GROUP: usize = 2;
164}
165#[cfg(not(feature = "7z010-7z007s-clg225"))]
166impl MisoPin for Pin<Mio41> {
167    const SPI: SpiId = SpiId::Spi0;
168    const GROUP: usize = 2;
169}
170#[cfg(not(feature = "7z010-7z007s-clg225"))]
171impl SsPin for Pin<Mio42> {
172    const SPI: SpiId = SpiId::Spi0;
173    const GROUP: usize = 2;
174    const IDX: usize = 0;
175}
176#[cfg(not(feature = "7z010-7z007s-clg225"))]
177impl SsPin for Pin<Mio43> {
178    const SPI: SpiId = SpiId::Spi0;
179    const GROUP: usize = 2;
180    const IDX: usize = 1;
181}
182#[cfg(not(feature = "7z010-7z007s-clg225"))]
183impl SsPin for Pin<Mio44> {
184    const SPI: SpiId = SpiId::Spi0;
185    const GROUP: usize = 2;
186    const IDX: usize = 2;
187}
188
189// SPI1, choice 1
190impl SckPin for Pin<Mio12> {
191    const SPI: SpiId = SpiId::Spi1;
192    const GROUP: usize = 0;
193}
194impl MosiPin for Pin<Mio10> {
195    const SPI: SpiId = SpiId::Spi1;
196    const GROUP: usize = 0;
197}
198impl MisoPin for Pin<Mio11> {
199    const SPI: SpiId = SpiId::Spi1;
200    const GROUP: usize = 0;
201}
202impl SsPin for Pin<Mio13> {
203    const SPI: SpiId = SpiId::Spi1;
204    const GROUP: usize = 0;
205    const IDX: usize = 0;
206}
207impl SsPin for Pin<Mio14> {
208    const SPI: SpiId = SpiId::Spi1;
209    const GROUP: usize = 0;
210    const IDX: usize = 1;
211}
212impl SsPin for Pin<Mio15> {
213    const SPI: SpiId = SpiId::Spi1;
214    const GROUP: usize = 0;
215    const IDX: usize = 2;
216}
217
218// SPI1, choice 2
219#[cfg(not(feature = "7z010-7z007s-clg225"))]
220impl SckPin for Pin<Mio24> {
221    const SPI: SpiId = SpiId::Spi1;
222    const GROUP: usize = 1;
223}
224#[cfg(not(feature = "7z010-7z007s-clg225"))]
225impl MosiPin for Pin<Mio22> {
226    const SPI: SpiId = SpiId::Spi1;
227    const GROUP: usize = 1;
228}
229#[cfg(not(feature = "7z010-7z007s-clg225"))]
230impl MisoPin for Pin<Mio23> {
231    const SPI: SpiId = SpiId::Spi1;
232    const GROUP: usize = 1;
233}
234#[cfg(not(feature = "7z010-7z007s-clg225"))]
235impl SsPin for Pin<Mio25> {
236    const SPI: SpiId = SpiId::Spi1;
237    const GROUP: usize = 1;
238    const IDX: usize = 0;
239}
240#[cfg(not(feature = "7z010-7z007s-clg225"))]
241impl SsPin for Pin<Mio26> {
242    const SPI: SpiId = SpiId::Spi1;
243    const GROUP: usize = 1;
244    const IDX: usize = 1;
245}
246#[cfg(not(feature = "7z010-7z007s-clg225"))]
247impl SsPin for Pin<Mio27> {
248    const SPI: SpiId = SpiId::Spi1;
249    const GROUP: usize = 1;
250    const IDX: usize = 2;
251}
252
253// SPI1, choice 2
254impl SckPin for Pin<Mio36> {
255    const SPI: SpiId = SpiId::Spi1;
256    const GROUP: usize = 2;
257}
258impl MosiPin for Pin<Mio34> {
259    const SPI: SpiId = SpiId::Spi1;
260    const GROUP: usize = 2;
261}
262impl MisoPin for Pin<Mio35> {
263    const SPI: SpiId = SpiId::Spi1;
264    const GROUP: usize = 2;
265}
266impl SsPin for Pin<Mio37> {
267    const SPI: SpiId = SpiId::Spi1;
268    const GROUP: usize = 2;
269    const IDX: usize = 0;
270}
271impl SsPin for Pin<Mio38> {
272    const SPI: SpiId = SpiId::Spi1;
273    const GROUP: usize = 2;
274    const IDX: usize = 1;
275}
276impl SsPin for Pin<Mio39> {
277    const SPI: SpiId = SpiId::Spi1;
278    const GROUP: usize = 2;
279    const IDX: usize = 2;
280}
281
282// SPI1, choice 3
283#[cfg(not(feature = "7z010-7z007s-clg225"))]
284impl SckPin for Pin<Mio48> {
285    const SPI: SpiId = SpiId::Spi1;
286    const GROUP: usize = 3;
287}
288#[cfg(not(feature = "7z010-7z007s-clg225"))]
289impl MosiPin for Pin<Mio46> {
290    const SPI: SpiId = SpiId::Spi1;
291    const GROUP: usize = 3;
292}
293#[cfg(not(feature = "7z010-7z007s-clg225"))]
294impl MisoPin for Pin<Mio47> {
295    const SPI: SpiId = SpiId::Spi1;
296    const GROUP: usize = 3;
297}
298#[cfg(not(feature = "7z010-7z007s-clg225"))]
299impl SsPin for Pin<Mio49> {
300    const SPI: SpiId = SpiId::Spi1;
301    const GROUP: usize = 3;
302    const IDX: usize = 0;
303}
304#[cfg(not(feature = "7z010-7z007s-clg225"))]
305impl SsPin for Pin<Mio50> {
306    const SPI: SpiId = SpiId::Spi1;
307    const GROUP: usize = 3;
308    const IDX: usize = 1;
309}
310#[cfg(not(feature = "7z010-7z007s-clg225"))]
311impl SsPin for Pin<Mio51> {
312    const SPI: SpiId = SpiId::Spi1;
313    const GROUP: usize = 3;
314    const IDX: usize = 2;
315}
316
317#[derive(Debug, PartialEq, Eq, Copy, Clone)]
318pub enum ChipSelect {
319    Slave0,
320    Slave1,
321    Slave2,
322    Decoded { cs0: bool, cs1: bool, cs2: bool },
323    None,
324}
325
326impl ChipSelect {
327    pub const fn raw_reg(&self) -> u4 {
328        u4::new(match self {
329            ChipSelect::Slave0 => 0b0000,
330            ChipSelect::Slave1 => 0b0001,
331            ChipSelect::Slave2 => 0b0010,
332            ChipSelect::None => 0b1111,
333            ChipSelect::Decoded { cs0, cs1, cs2 } => {
334                (1 << 3) | (*cs0 as u8) | ((*cs1 as u8) << 1) | ((*cs2 as u8) << 2)
335            }
336        })
337    }
338
339    #[inline]
340    pub const fn cs_no_ext_decoding(&self, raw: u4) -> Option<ChipSelect> {
341        let val = raw.value();
342        if val == 0b1111 {
343            return Some(ChipSelect::None);
344        }
345        if val & 0b1 == 0 {
346            return Some(ChipSelect::Slave0);
347        } else if val & 0b11 == 0b01 {
348            return Some(ChipSelect::Slave1);
349        } else if val & 0b111 == 0b010 {
350            return Some(ChipSelect::Slave2);
351        }
352        None
353    }
354}
355
356#[derive(Default, Debug, Clone, Copy, PartialEq, Eq)]
357/// Slave select configuration.
358pub enum SlaveSelectConfig {
359    /// User must take care of controlling slave select lines as well as issuing a start command.
360    ManualWithManualStart = 0b11,
361    ManualAutoStart = 0b10,
362    /// Hardware slave select, but start needs to be issued manually.
363    AutoWithManualStart = 0b01,
364    /// Hardware slave select, auto serialiation if there is data in the TX FIFO.
365    #[default]
366    AutoWithAutoStart = 0b00,
367}
368
369#[derive(Debug, Copy, Clone)]
370pub struct Config {
371    baud_div: BaudDivSel,
372    init_mode: Mode,
373    ss_config: SlaveSelectConfig,
374    with_ext_decoding: bool,
375}
376
377impl Config {
378    pub fn new(baud_div: BaudDivSel, init_mode: Mode, ss_config: SlaveSelectConfig) -> Self {
379        Self {
380            baud_div,
381            init_mode,
382            ss_config,
383            with_ext_decoding: false,
384        }
385    }
386
387    pub fn enable_external_decoding(&mut self) {
388        self.with_ext_decoding = true;
389    }
390}
391
392/// Thin re-usable low-level helper to interface with the SPI.
393pub struct SpiLowLevel {
394    id: SpiId,
395    regs: zynq7000::spi::MmioSpi<'static>,
396}
397
398impl SpiLowLevel {
399    /// Steal the SPI low level helper.
400    ///
401    /// # Safety
402    ///
403    /// This API can be used to potentially create a driver to the same peripheral structure
404    /// from multiple threads. The user must ensure that concurrent accesses are safe and do not
405    /// interfere with each other.
406    pub unsafe fn steal(id: SpiId) -> Self {
407        let regs = unsafe {
408            match id {
409                SpiId::Spi0 => zynq7000::spi::Spi::new_mmio_fixed_0(),
410                SpiId::Spi1 => zynq7000::spi::Spi::new_mmio_fixed_1(),
411            }
412        };
413        Self { id, regs }
414    }
415
416    /// Clone the SPI low level helper.
417    ///
418    /// # Safety
419    ///
420    /// This API can be used to potentially create a driver to the same peripheral structure
421    /// from multiple threads. The user must ensure that concurrent accesses are safe and do not
422    /// interfere with each other.
423    pub unsafe fn clone(&self) -> Self {
424        Self {
425            id: self.id,
426            regs: unsafe { self.regs.clone() },
427        }
428    }
429
430    pub fn id(&self) -> SpiId {
431        self.id
432    }
433
434    #[inline]
435    pub fn disable(&mut self) {
436        self.regs.write_enable(0);
437    }
438
439    #[inline]
440    pub fn enable(&mut self) {
441        self.regs.write_enable(1);
442    }
443
444    /// Select the peripheral chip select line.
445    ///
446    /// This needs to be done before starting a transfer to select the correct peripheral chip
447    /// select line.
448    ///
449    /// The decoder bits logic is is based
450    /// [on online documentation](https://www.realdigital.org/doc/3eb4f7a05e5003f2e0e6858a27a679bb?utm_source=chatgpt.com)
451    /// because the TRM does not specify how decoding really works. This also only works if
452    /// the external decoding was enabled via the [Config::enable_external_decoding] option.
453    #[inline]
454    pub fn select_hw_cs(&mut self, chip_select: ChipSelect) {
455        self.regs.modify_cr(|mut val| {
456            val.set_cs_raw(chip_select.raw_reg());
457            val
458        });
459    }
460
461    /// Re-configures the mode register.
462    #[inline]
463    pub fn configure_mode(&mut self, mode: Mode) {
464        let (cpol, cpha) = spi_mode_const_to_cpol_cpha(mode);
465        self.regs.modify_cr(|mut val| {
466            val.set_cpha(cpha);
467            val.set_cpol(cpol);
468            val
469        });
470    }
471
472    /// Re-configures the delay control register.
473    #[inline]
474    pub fn configure_delays(&mut self, config: DelayControl) {
475        self.regs.write_delay_control(config)
476    }
477
478    /// Re-configures the SPI peripheral with the initial configuration.
479    pub fn reconfigure(&mut self, config: Config) {
480        self.regs.write_enable(0);
481        let (man_ss, man_start) = match config.ss_config {
482            SlaveSelectConfig::ManualWithManualStart => (true, true),
483            SlaveSelectConfig::ManualAutoStart => (true, false),
484            SlaveSelectConfig::AutoWithManualStart => (false, true),
485            SlaveSelectConfig::AutoWithAutoStart => (false, false),
486        };
487        let (cpol, cpha) = spi_mode_const_to_cpol_cpha(config.init_mode);
488
489        self.regs.write_cr(
490            zynq7000::spi::Config::builder()
491                .with_modefail_gen_en(false)
492                .with_manual_start(false)
493                .with_manual_start_enable(man_start)
494                .with_manual_cs(man_ss)
495                .with_cs_raw(ChipSelect::None.raw_reg())
496                .with_peri_sel(config.with_ext_decoding)
497                .with_baud_rate_div(config.baud_div)
498                .with_cpha(cpha)
499                .with_cpol(cpol)
500                .with_master_ern(true)
501                .build(),
502        );
503        // Configures for polling mode by default: TX trigger by one will lead to the
504        // TX FIFO not full signal being set when the TX FIFO is emtpy.
505        self.regs.write_tx_trig(1);
506        // Same as TX.
507        self.regs.write_rx_trig(1);
508
509        self.regs.write_enable(1);
510    }
511
512    /// [Resets][reset] and [re-configures][Self::reconfigure] the SPI peripheral.
513    ///
514    /// This can also be used to reset the FIFOs and the state machine of the SPI.
515    pub fn reset_and_reconfigure(&mut self, config: Config) {
516        reset(self.id());
517        self.reconfigure(config);
518    }
519
520    /// No peripheral slave selection.
521    #[inline]
522    pub fn no_hw_cs(&mut self) {
523        self.select_hw_cs(ChipSelect::None);
524    }
525
526    #[inline(always)]
527    pub fn write_fifo_unchecked(&mut self, data: u8) {
528        self.regs.write_txd(FifoWrite::new(data));
529    }
530
531    #[inline(always)]
532    pub fn read_fifo_unchecked(&mut self) -> u8 {
533        self.regs.read_rxd().value()
534    }
535
536    #[inline]
537    pub fn issue_manual_start(&mut self) {
538        self.regs.modify_cr(|mut val| {
539            val.set_manual_start(true);
540            val
541        });
542    }
543
544    #[inline]
545    pub fn read_isr(&self) -> InterruptStatus {
546        self.regs.read_isr()
547    }
548
549    #[inline]
550    pub fn read_imr(&self) -> InterruptMask {
551        self.regs.read_imr()
552    }
553
554    #[inline]
555    pub fn read_rx_not_empty_threshold(&self) -> u32 {
556        self.regs.read_rx_trig()
557    }
558
559    #[inline]
560    pub fn set_rx_fifo_trigger(&mut self, trigger: u32) -> Result<(), InvalidTriggerError> {
561        if trigger > FIFO_DEPTH as u32 {
562            return Err(InvalidTriggerError(trigger as usize));
563        }
564        self.regs.write_rx_trig(trigger.value());
565        Ok(())
566    }
567
568    #[inline]
569    pub fn set_tx_fifo_trigger(&mut self, trigger: u32) -> Result<(), InvalidTriggerError> {
570        if trigger > FIFO_DEPTH as u32 {
571            return Err(InvalidTriggerError(trigger as usize));
572        }
573        self.regs.write_tx_trig(trigger.value());
574        Ok(())
575    }
576
577    /// This disables all interrupts relevant for non-blocking interrupt driven SPI operation
578    /// in SPI master mode.
579    #[inline]
580    pub fn disable_interrupts(&mut self) {
581        self.regs.write_idr(
582            InterruptControl::builder()
583                .with_tx_underflow(true)
584                .with_rx_full(true)
585                .with_rx_not_empty(true)
586                .with_tx_full(false)
587                .with_tx_trig(true)
588                .with_mode_fault(false)
589                .with_rx_ovr(true)
590                .build(),
591        );
592    }
593
594    /// This enables all interrupts relevant for non-blocking interrupt driven SPI operation
595    /// in SPI master mode.
596    #[inline]
597    pub fn enable_interrupts(&mut self) {
598        self.regs.write_ier(
599            InterruptControl::builder()
600                .with_tx_underflow(true)
601                .with_rx_full(true)
602                .with_rx_not_empty(true)
603                .with_tx_full(false)
604                .with_tx_trig(true)
605                .with_mode_fault(false)
606                .with_rx_ovr(true)
607                .build(),
608        );
609    }
610
611    /// This clears all interrupts relevant for non-blocking interrupt driven SPI operation
612    /// in SPI master mode.
613    #[inline]
614    pub fn clear_interrupts(&mut self) {
615        self.regs.write_isr(
616            InterruptStatus::builder()
617                .with_tx_underflow(true)
618                .with_rx_full(true)
619                .with_rx_not_empty(true)
620                .with_tx_full(false)
621                .with_tx_not_full(true)
622                .with_mode_fault(false)
623                .with_rx_ovr(true)
624                .build(),
625        );
626    }
627}
628
629/// Blocking Driver for the PS SPI peripheral in master mode.
630pub struct Spi {
631    inner: SpiLowLevel,
632    sclk: Hertz,
633    config: Config,
634    outstanding_rx: bool,
635}
636
637#[derive(Debug, thiserror::Error)]
638#[error("invalid SPI ID")]
639pub struct InvalidPsSpiError;
640
641#[derive(Debug, thiserror::Error)]
642#[error("invalid trigger value {0}")]
643pub struct InvalidTriggerError(pub usize);
644
645// TODO: Add and handle MUX config check.
646#[derive(Debug, thiserror::Error)]
647pub enum SpiConstructionError {
648    #[error("invalid SPI ID {0}")]
649    InvalidPsSpi(#[from] InvalidPsSpiError),
650    /// The specified pins are not compatible to the specified SPI peripheral.
651    #[error("pin invalid for SPI ID")]
652    PinInvalidForSpiId,
653    /// The specified pins are not from the same pin group.
654    #[error("pin group missmatch")]
655    GroupMissmatch,
656    #[error("invalid pin configuration for SPI")]
657    InvalidPinConf,
658}
659
660impl Spi {
661    pub fn new_no_hw_ss<Sck: SckPin, Mosi: MosiPin, Miso: MisoPin>(
662        spi: impl PsSpi,
663        clocks: &IoClocks,
664        config: Config,
665        spi_pins: (Sck, Mosi, Miso),
666    ) -> Result<Self, SpiConstructionError> {
667        let spi_id = spi.id();
668        if spi_id.is_none() {
669            return Err(InvalidPsSpiError.into());
670        }
671        let spi_id = spi_id.unwrap();
672        if Sck::GROUP != Mosi::GROUP || Sck::GROUP != Miso::GROUP {
673            return Err(SpiConstructionError::GroupMissmatch);
674        }
675        if Sck::SPI != spi_id || Mosi::SPI != spi_id || Miso::SPI != spi_id {
676            return Err(SpiConstructionError::PinInvalidForSpiId);
677        }
678        IoPeriphPin::new(spi_pins.0, SPI_MUX_CONF, Some(false));
679        IoPeriphPin::new(spi_pins.1, SPI_MUX_CONF, Some(false));
680        IoPeriphPin::new(spi_pins.2, SPI_MUX_CONF, Some(false));
681        Ok(Self::new_generic_unchecked(
682            spi_id,
683            spi.reg_block(),
684            clocks,
685            config,
686        ))
687    }
688
689    pub fn new_one_hw_cs<Sck: SckPin, Mosi: MosiPin, Miso: MisoPin, Ss: SsPin>(
690        spi: impl PsSpi,
691        clocks: &IoClocks,
692        config: Config,
693        spi_pins: (Sck, Mosi, Miso),
694        ss_pin: Ss,
695    ) -> Result<Self, SpiConstructionError> {
696        let spi_id = spi.id();
697        if spi_id.is_none() {
698            return Err(InvalidPsSpiError.into());
699        }
700        let spi_id = spi_id.unwrap();
701        if Sck::GROUP != Mosi::GROUP || Sck::GROUP != Miso::GROUP || Sck::GROUP != Ss::GROUP {
702            return Err(SpiConstructionError::GroupMissmatch);
703        }
704        if Sck::SPI != spi_id || Mosi::SPI != spi_id || Miso::SPI != spi_id || Ss::SPI != spi_id {
705            return Err(SpiConstructionError::PinInvalidForSpiId);
706        }
707        IoPeriphPin::new(spi_pins.0, SPI_MUX_CONF, Some(false));
708        IoPeriphPin::new(spi_pins.1, SPI_MUX_CONF, Some(false));
709        IoPeriphPin::new(spi_pins.2, SPI_MUX_CONF, Some(false));
710        IoPeriphPin::new(ss_pin, SPI_MUX_CONF, Some(false));
711        Ok(Self::new_generic_unchecked(
712            spi_id,
713            spi.reg_block(),
714            clocks,
715            config,
716        ))
717    }
718
719    pub fn new_with_two_hw_cs<Sck: SckPin, Mosi: MosiPin, Miso: MisoPin, Ss0: SsPin, Ss1: SsPin>(
720        spi: impl PsSpi,
721        clocks: &IoClocks,
722        config: Config,
723        spi_pins: (Sck, Mosi, Miso),
724        ss_pins: (Ss0, Ss1),
725    ) -> Result<Self, SpiConstructionError> {
726        let spi_id = spi.id();
727        if spi_id.is_none() {
728            return Err(InvalidPsSpiError.into());
729        }
730        let spi_id = spi_id.unwrap();
731        if Sck::GROUP != Mosi::GROUP
732            || Sck::GROUP != Miso::GROUP
733            || Sck::GROUP != Ss0::GROUP
734            || Sck::GROUP != Ss1::GROUP
735        {
736            return Err(SpiConstructionError::GroupMissmatch);
737        }
738        if Sck::SPI != spi_id
739            || Mosi::SPI != spi_id
740            || Miso::SPI != spi_id
741            || Ss0::SPI != spi_id
742            || Ss1::SPI != spi_id
743        {
744            return Err(SpiConstructionError::PinInvalidForSpiId);
745        }
746        IoPeriphPin::new(spi_pins.0, SPI_MUX_CONF, Some(false));
747        IoPeriphPin::new(spi_pins.1, SPI_MUX_CONF, Some(false));
748        IoPeriphPin::new(spi_pins.2, SPI_MUX_CONF, Some(false));
749        IoPeriphPin::new(ss_pins.0, SPI_MUX_CONF, Some(false));
750        IoPeriphPin::new(ss_pins.1, SPI_MUX_CONF, Some(false));
751        Ok(Self::new_generic_unchecked(
752            spi_id,
753            spi.reg_block(),
754            clocks,
755            config,
756        ))
757    }
758
759    pub fn new_with_three_hw_cs<
760        Sck: SckPin,
761        Mosi: MosiPin,
762        Miso: MisoPin,
763        Ss0: SsPin,
764        Ss1: SsPin,
765        Ss2: SsPin,
766    >(
767        spi: impl PsSpi,
768        clocks: &IoClocks,
769        config: Config,
770        spi_pins: (Sck, Mosi, Miso),
771        ss_pins: (Ss0, Ss1, Ss2),
772    ) -> Result<Self, SpiConstructionError> {
773        let spi_id = spi.id();
774        if spi_id.is_none() {
775            return Err(InvalidPsSpiError.into());
776        }
777        let spi_id = spi_id.unwrap();
778        if Sck::GROUP != Mosi::GROUP
779            || Sck::GROUP != Miso::GROUP
780            || Sck::GROUP != Ss0::GROUP
781            || Sck::GROUP != Ss1::GROUP
782            || Sck::GROUP != Ss2::GROUP
783        {
784            return Err(SpiConstructionError::GroupMissmatch);
785        }
786        if Sck::SPI != spi_id
787            || Mosi::SPI != spi_id
788            || Miso::SPI != spi_id
789            || Ss0::SPI != spi_id
790            || Ss1::SPI != spi_id
791            || Ss2::SPI != spi_id
792        {
793            return Err(SpiConstructionError::PinInvalidForSpiId);
794        }
795        IoPeriphPin::new(spi_pins.0, SPI_MUX_CONF, Some(false));
796        IoPeriphPin::new(spi_pins.1, SPI_MUX_CONF, Some(false));
797        IoPeriphPin::new(spi_pins.2, SPI_MUX_CONF, Some(false));
798        IoPeriphPin::new(ss_pins.0, SPI_MUX_CONF, Some(false));
799        IoPeriphPin::new(ss_pins.1, SPI_MUX_CONF, Some(false));
800        IoPeriphPin::new(ss_pins.2, SPI_MUX_CONF, Some(false));
801        Ok(Self::new_generic_unchecked(
802            spi_id,
803            spi.reg_block(),
804            clocks,
805            config,
806        ))
807    }
808
809    pub fn new_generic_unchecked(
810        id: SpiId,
811        regs: MmioSpi<'static>,
812        clocks: &IoClocks,
813        config: Config,
814    ) -> Self {
815        let periph_sel = match id {
816            SpiId::Spi0 => crate::PeriphSelect::Spi0,
817            SpiId::Spi1 => crate::PeriphSelect::Spi1,
818        };
819        enable_amba_peripheral_clock(periph_sel);
820        let sclk = clocks.spi_clk() / config.baud_div.div_value() as u32;
821        let mut spi = Self {
822            inner: SpiLowLevel { regs, id },
823            sclk,
824            config,
825            outstanding_rx: false,
826        };
827        spi.reset_and_reconfigure();
828        spi
829    }
830
831    /// Re-configures the SPI peripheral with the initial configuration.
832    pub fn reconfigure(&mut self) {
833        self.inner.reconfigure(self.config);
834    }
835
836    /// [Resets][reset] and [re-configures][Self::reconfigure] the SPI peripheral.
837    ///
838    /// This can also be used to reset the FIFOs and the state machine of the SPI.
839    pub fn reset_and_reconfigure(&mut self) {
840        reset(self.inner.id());
841        self.reconfigure();
842    }
843
844    #[inline]
845    pub fn issue_manual_start_for_manual_cfg(&mut self) {
846        if self.config.ss_config == SlaveSelectConfig::AutoWithManualStart
847            || self.config.ss_config == SlaveSelectConfig::ManualWithManualStart
848        {
849            self.inner.issue_manual_start();
850        }
851    }
852
853    /// Retrieve SCLK clock frequency currently configured for this SPI.
854    #[inline]
855    pub const fn sclk(&self) -> Hertz {
856        self.sclk
857    }
858
859    /// Retrieve inner low-level helper.
860    #[inline]
861    pub const fn inner(&mut self) -> &mut SpiLowLevel {
862        &mut self.inner
863    }
864
865    #[inline]
866    pub fn regs(&mut self) -> &mut MmioSpi<'static> {
867        &mut self.inner.regs
868    }
869
870    fn initial_fifo_fill(&mut self, words: &[u8]) -> usize {
871        let write_len = core::cmp::min(FIFO_DEPTH, words.len());
872        (0..write_len).for_each(|idx| {
873            self.inner.write_fifo_unchecked(words[idx]);
874        });
875        write_len
876    }
877
878    fn prepare_generic_blocking_transfer(&mut self, words: &[u8]) -> usize {
879        // We want to ensure the FIFO is empty for a new transfer. This is the simpler
880        // implementation for now.
881        self.flush().unwrap();
882        // Write this to 1 in any case to allow polling, defensive programming.
883        self.inner.regs.write_rx_trig(1);
884
885        // Fill the FIFO with initial data.
886        let written = self.initial_fifo_fill(words);
887
888        // We assume that the slave select configuration was already performed, but we take
889        // care of issuing a start if necessary.
890        self.issue_manual_start_for_manual_cfg();
891        written
892    }
893}
894
895impl embedded_hal::spi::ErrorType for Spi {
896    type Error = Infallible;
897}
898
899impl embedded_hal::spi::SpiBus for Spi {
900    fn read(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
901        if words.is_empty() {
902            return Ok(());
903        }
904        // We want to ensure the FIFO is empty for a new transfer. This is the simpler
905        // implementation for now.
906        self.flush()?;
907        // Write this to 1 in any case to allow polling, defensive programming.
908        self.regs().write_rx_trig(1);
909
910        let mut write_idx = core::cmp::min(FIFO_DEPTH, words.len());
911        // Send dummy bytes.
912        (0..write_idx).for_each(|_| {
913            self.inner.write_fifo_unchecked(0);
914        });
915
916        // We assume that the slave select configuration was already performed, but we take
917        // care of issuing a start if necessary.
918        self.issue_manual_start_for_manual_cfg();
919
920        let mut read_idx = 0;
921        while read_idx < words.len() {
922            let status = self.regs().read_isr();
923            if status.rx_not_empty() {
924                words[read_idx] = self.inner.read_fifo_unchecked();
925                read_idx += 1;
926            }
927            // Continue pumping the FIFO if necesary and possible.
928            if write_idx < words.len() && !status.tx_full() {
929                self.inner.write_fifo_unchecked(0);
930                write_idx += 1;
931            }
932        }
933
934        Ok(())
935    }
936
937    fn write(&mut self, words: &[u8]) -> Result<(), Self::Error> {
938        if words.is_empty() {
939            return Ok(());
940        }
941        let mut written = self.prepare_generic_blocking_transfer(words);
942        let mut read_idx = 0;
943
944        while written < words.len() {
945            let status = self.regs().read_isr();
946            // We empty the FIFO to prevent it filling up completely, as long as we have to write
947            // bytes
948            if status.rx_not_empty() {
949                self.inner.read_fifo_unchecked();
950                read_idx += 1;
951            }
952            if !status.tx_full() {
953                self.inner.write_fifo_unchecked(words[written]);
954                written += 1;
955            }
956        }
957        // We exit once all bytes have been written, so some bytes to read might be outstanding.
958        // We use the FIFO trigger mechanism to determine when we can read all the remaining bytes.
959        self.regs().write_rx_trig((words.len() - read_idx) as u32);
960        self.outstanding_rx = true;
961        Ok(())
962    }
963
964    fn transfer(&mut self, read: &mut [u8], write: &[u8]) -> Result<(), Self::Error> {
965        if read.is_empty() {
966            return Ok(());
967        }
968        let mut write_idx = self.prepare_generic_blocking_transfer(write);
969        let mut read_idx = 0;
970        let max_idx = core::cmp::max(write.len(), read.len());
971
972        let mut writes_finished = write_idx == max_idx;
973        let mut reads_finished = false;
974        while !writes_finished || !reads_finished {
975            let status = self.regs().read_isr();
976            if status.rx_not_empty() && !reads_finished {
977                if read_idx < read.len() {
978                    read[read_idx] = self.inner.read_fifo_unchecked();
979                } else {
980                    // Discard the data.
981                    self.inner.read_fifo_unchecked();
982                }
983                read_idx += 1;
984            }
985
986            if !status.tx_full() && !writes_finished {
987                if write_idx < write.len() {
988                    self.inner.write_fifo_unchecked(write[write_idx]);
989                } else {
990                    // Send dummy bytes.
991                    self.inner.write_fifo_unchecked(0);
992                }
993                write_idx += 1;
994            }
995            writes_finished = write_idx == max_idx;
996            reads_finished = read_idx == max_idx;
997        }
998
999        Ok(())
1000    }
1001
1002    fn transfer_in_place(&mut self, words: &mut [u8]) -> Result<(), Self::Error> {
1003        if words.is_empty() {
1004            return Ok(());
1005        }
1006        let mut write_idx = self.prepare_generic_blocking_transfer(words);
1007        let mut read_idx = 0;
1008
1009        let mut writes_finished = write_idx == words.len();
1010        let mut reads_finished = false;
1011        while !writes_finished || !reads_finished {
1012            let status = self.inner.read_isr();
1013            if status.rx_not_empty() && !reads_finished {
1014                words[read_idx] = self.inner.read_fifo_unchecked();
1015                read_idx += 1;
1016            }
1017
1018            if !status.tx_full() && !writes_finished {
1019                self.inner.write_fifo_unchecked(words[write_idx]);
1020                write_idx += 1;
1021            }
1022            writes_finished = write_idx == words.len();
1023            reads_finished = read_idx == words.len();
1024        }
1025
1026        Ok(())
1027    }
1028
1029    /// Blocking flush implementation.
1030    fn flush(&mut self) -> Result<(), Self::Error> {
1031        if !self.outstanding_rx {
1032            return Ok(());
1033        }
1034        let rx_trig = self.inner.read_rx_not_empty_threshold();
1035        while !self.inner.read_isr().rx_not_empty() {}
1036        (0..rx_trig).for_each(|_| {
1037            self.inner.read_fifo_unchecked();
1038        });
1039        self.inner.set_rx_fifo_trigger(1).unwrap();
1040        self.outstanding_rx = false;
1041        Ok(())
1042    }
1043}
1044
1045pub struct SpiWithHwCs<Delay: DelayNs> {
1046    pub spi: Spi,
1047    pub cs: ChipSelect,
1048    pub delay: Delay,
1049}
1050
1051impl<Delay: DelayNs> SpiWithHwCs<Delay> {
1052    pub fn new(spi: Spi, cs: ChipSelect, delay: Delay) -> Self {
1053        Self { spi, cs, delay }
1054    }
1055
1056    pub fn release(self) -> Spi {
1057        self.spi
1058    }
1059}
1060
1061impl<Delay: DelayNs> embedded_hal::spi::ErrorType for SpiWithHwCs<Delay> {
1062    type Error = Infallible;
1063}
1064
1065impl<Delay: DelayNs> embedded_hal::spi::SpiDevice for SpiWithHwCs<Delay> {
1066    fn transaction(
1067        &mut self,
1068        operations: &mut [embedded_hal::spi::Operation<'_, u8>],
1069    ) -> Result<(), Self::Error> {
1070        self.spi.inner.select_hw_cs(self.cs);
1071        for op in operations {
1072            match op {
1073                embedded_hal::spi::Operation::Read(items) => {
1074                    self.spi.read(items)?;
1075                }
1076                embedded_hal::spi::Operation::Write(items) => {
1077                    self.spi.write(items)?;
1078                }
1079                embedded_hal::spi::Operation::Transfer(read, write) => {
1080                    self.spi.transfer(read, write)?;
1081                }
1082                embedded_hal::spi::Operation::TransferInPlace(items) => {
1083                    self.spi.transfer_in_place(items)?;
1084                }
1085                embedded_hal::spi::Operation::DelayNs(delay) => {
1086                    self.delay.delay_ns(*delay);
1087                }
1088            }
1089        }
1090        self.spi.flush()?;
1091        self.spi.inner.no_hw_cs();
1092        Ok(())
1093    }
1094}
1095
1096/// Reset the SPI peripheral using the SLCR reset register for SPI.
1097///
1098/// Please note that this function will interfere with an already configured
1099/// SPI instance.
1100#[inline]
1101pub fn reset(id: SpiId) {
1102    let assert_reset = match id {
1103        SpiId::Spi0 => DualRefAndClockReset::builder()
1104            .with_periph1_ref_rst(false)
1105            .with_periph0_ref_rst(true)
1106            .with_periph1_cpu1x_rst(false)
1107            .with_periph0_cpu1x_rst(true)
1108            .build(),
1109        SpiId::Spi1 => DualRefAndClockReset::builder()
1110            .with_periph1_ref_rst(true)
1111            .with_periph0_ref_rst(false)
1112            .with_periph1_cpu1x_rst(true)
1113            .with_periph0_cpu1x_rst(false)
1114            .build(),
1115    };
1116    unsafe {
1117        Slcr::with(|regs| {
1118            regs.reset_ctrl().write_spi(assert_reset);
1119            // Keep it in reset for some cycles.. The TMR just mentions some small delay,
1120            // no idea what is meant with that.
1121            for _ in 0..3 {
1122                cortex_ar::asm::nop();
1123            }
1124            regs.reset_ctrl().write_spi(DualRefAndClockReset::DEFAULT);
1125        });
1126    }
1127}
1128
1129/// Calculates the largest allowed SPI reference clock divisor.
1130///
1131/// The Zynq7000 SPI peripheral has the following requirement for the SPI reference clock:
1132/// It must be larger than the CPU 1X clock. Therefore, the divisor used to calculate the reference
1133/// clock has a maximum value, which can be calculated with this function.
1134///
1135/// [configure_spi_ref_clk] can be used to configure the SPI reference clock with the calculated
1136/// value.
1137pub fn calculate_largest_allowed_spi_ref_clk_divisor(clks: &Clocks) -> Option<u6> {
1138    let slcr = unsafe { Slcr::steal() };
1139    let spi_clk_ctrl = slcr.regs().clk_ctrl_shared().read_spi_clk_ctrl();
1140    let div = match spi_clk_ctrl.srcsel() {
1141        zynq7000::slcr::clocks::SrcSelIo::IoPll | zynq7000::slcr::clocks::SrcSelIo::IoPllAlt => {
1142            clks.io_clocks().ref_clk() / clks.arm_clocks().cpu_1x_clk()
1143        }
1144        zynq7000::slcr::clocks::SrcSelIo::ArmPll => {
1145            clks.arm_clocks().ref_clk() / clks.arm_clocks().cpu_1x_clk()
1146        }
1147        zynq7000::slcr::clocks::SrcSelIo::DdrPll => {
1148            clks.ddr_clocks().ref_clk() / clks.arm_clocks().cpu_1x_clk()
1149        }
1150    };
1151    if div > u6::MAX.value() as u32 {
1152        return None;
1153    }
1154
1155    Some(u6::new(div as u8))
1156}
1157
1158pub fn configure_spi_ref_clk(clks: &mut Clocks, divisor: u6) {
1159    let mut slcr = unsafe { Slcr::steal() };
1160    let spi_clk_ctrl = slcr.regs().clk_ctrl_shared().read_spi_clk_ctrl();
1161    slcr.modify(|regs| {
1162        regs.clk_ctrl().modify_spi_clk_ctrl(|mut val| {
1163            val.set_divisor(divisor);
1164            val
1165        });
1166    });
1167    let new_clk = match spi_clk_ctrl.srcsel() {
1168        zynq7000::slcr::clocks::SrcSelIo::IoPll | zynq7000::slcr::clocks::SrcSelIo::IoPllAlt => {
1169            clks.io_clocks().ref_clk() / divisor.value() as u32
1170        }
1171        zynq7000::slcr::clocks::SrcSelIo::ArmPll => {
1172            clks.arm_clocks().ref_clk() / divisor.value() as u32
1173        }
1174        zynq7000::slcr::clocks::SrcSelIo::DdrPll => {
1175            clks.ddr_clocks().ref_clk() / divisor.value() as u32
1176        }
1177    };
1178    clks.io_clocks_mut().update_spi_clk(new_clk);
1179}