Skip to main content

vorago_shared_hal/spi/
mod.rs

1use crate::FunctionSelect;
2use crate::gpio::{DynPinId, IoPeriphPin};
3use crate::{PeripheralSelect, enable_peripheral_clock, pins::AnyPin, sealed::Sealed, time::Hertz};
4use core::{convert::Infallible, fmt::Debug, marker::PhantomData};
5use embedded_hal::spi::{MODE_0, Mode};
6
7use regs::{ClockPrescaler, Data, FifoClear, WordSize};
8#[cfg(feature = "vor1x")]
9use va108xx as pac;
10#[cfg(feature = "vor4x")]
11use va416xx as pac;
12
13pub use regs::{Bank, HwChipSelectId};
14
15pub mod asynch;
16pub mod regs;
17
18pub const FIFO_DEPTH: usize = 16;
19
20pub fn configure_pin_as_hw_cs_pin<P: AnyPin + HwCsProvider>(_pin: P) -> HwChipSelectId {
21    IoPeriphPin::new(P::ID, P::FUN_SEL, None);
22    P::CS_ID
23}
24
25//==================================================================================================
26// Pins and traits.
27//==================================================================================================
28
29pub trait PinSck0: AnyPin {
30    const SPI_ID: Bank = Bank::Spi0;
31    const FUN_SEL: FunctionSelect;
32}
33
34pub trait PinMosi0: AnyPin {
35    const SPI_ID: Bank = Bank::Spi0;
36    const FUN_SEL: FunctionSelect;
37}
38
39pub trait PinMiso0: AnyPin {
40    const SPI_ID: Bank = Bank::Spi0;
41    const FUN_SEL: FunctionSelect;
42}
43
44pub trait PinSck1: AnyPin {
45    const SPI_ID: Bank = Bank::Spi1;
46    const FUN_SEL: FunctionSelect;
47}
48
49pub trait PinMosi1: AnyPin {
50    const SPI_ID: Bank = Bank::Spi1;
51    const FUN_SEL: FunctionSelect;
52}
53
54pub trait PinMiso1: AnyPin {
55    const SPI_ID: Bank = Bank::Spi1;
56    const FUN_SEL: FunctionSelect;
57}
58
59pub trait PinSck2: AnyPin {
60    const SPI_ID: Bank = Bank::Spi2;
61    const FUN_SEL: FunctionSelect;
62}
63
64pub trait PinMosi2: AnyPin {
65    const SPI_ID: Bank = Bank::Spi2;
66    const FUN_SEL: FunctionSelect;
67}
68
69pub trait PinMiso2: AnyPin {
70    const SPI_ID: Bank = Bank::Spi2;
71    const FUN_SEL: FunctionSelect;
72}
73
74pub trait HwCsProvider {
75    const PIN_ID: DynPinId;
76    const SPI_ID: Bank;
77    const FUN_SEL: FunctionSelect;
78    const CS_ID: HwChipSelectId;
79}
80
81#[macro_use]
82mod macros {
83    #[cfg(not(feature = "va41628"))]
84    macro_rules! hw_cs_multi_pin {
85    (
86        // name of the newtype wrapper struct
87        $name:ident,
88        // Pb0
89        $pin_id:ident,
90        // SpiId::B
91        $spi_id:path,
92        // FunSel::Sel1
93        $fun_sel:path,
94        // HwChipSelectId::Id2
95        $cs_id:path
96    ) => {
97            #[doc = concat!(
98                "Newtype wrapper to use [Pin] [`", stringify!($pin_id), "`] as a HW CS pin for [`", stringify!($spi_id), "`] with [`", stringify!($cs_id), "`]."
99            )]
100            pub struct $name(Pin<$pin_id>);
101
102            impl $name {
103                pub fn new(pin: Pin<$pin_id>) -> Self {
104                    Self(pin)
105                }
106            }
107
108            impl crate::sealed::Sealed for $name {}
109
110            impl AnyPin for $name {
111                const ID: DynPinId = <$pin_id as PinId>::ID;
112            }
113
114            impl HwCsProvider for $name {
115                const PIN_ID: DynPinId = <$pin_id as PinId>::ID;
116                const SPI_ID: Bank = $spi_id;
117                const FUN_SEL: FunctionSelect = $fun_sel;
118                const CS_ID: HwChipSelectId = $cs_id;
119            }
120        };
121    }
122
123    #[macro_export]
124    macro_rules! hw_cs_pins {
125        ($SpiId:path, $(($Px:ident, $FunSel:path, $HwCsIdent:path)$(,)?)+) => {
126            $(
127                impl HwCsProvider for Pin<$Px> {
128                    const PIN_ID: DynPinId = $Px::ID;
129                    const SPI_ID: Bank = $SpiId;
130                    const FUN_SEL: FunctionSelect = $FunSel;
131                    const CS_ID: HwChipSelectId = $HwCsIdent;
132                }
133            )+
134        };
135    }
136}
137
138#[cfg(feature = "vor1x")]
139pub mod pins_vor1x;
140#[cfg(feature = "vor4x")]
141pub mod pins_vor4x;
142
143//==================================================================================================
144// Defintions
145//==================================================================================================
146
147// FIFO has a depth of 16.
148const FILL_DEPTH: usize = 12;
149
150pub const BMSTART_BMSTOP_MASK: u32 = 1 << 31;
151pub const BMSKIPDATA_MASK: u32 = 1 << 30;
152
153pub const DEFAULT_CLK_DIV: u16 = 2;
154
155pub trait Spi0Instance: Sealed {
156    const ID: Bank = Bank::Spi0;
157    const PERIPH_SEL: PeripheralSelect;
158}
159
160pub trait Spi1Instance: Sealed {
161    const ID: Bank = Bank::Spi1;
162    const PERIPH_SEL: PeripheralSelect;
163}
164
165pub trait Spi2Instance: Sealed {
166    const ID: Bank = Bank::Spi2;
167    const PERIPH_SEL: PeripheralSelect;
168}
169
170#[cfg(feature = "vor4x")]
171pub trait Spi3Instance: Sealed {
172    const ID: Bank = Bank::Spi3;
173    const PERIPH_SEL: PeripheralSelect;
174}
175
176#[cfg(feature = "vor1x")]
177pub type Spi0 = pac::Spia;
178#[cfg(feature = "vor4x")]
179pub type Spi0 = pac::Spi0;
180
181impl Spi0Instance for Spi0 {
182    const ID: Bank = Bank::Spi0;
183    const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi0;
184}
185impl Sealed for Spi0 {}
186
187#[cfg(feature = "vor1x")]
188pub type Spi1 = pac::Spib;
189#[cfg(feature = "vor4x")]
190pub type Spi1 = pac::Spi1;
191
192impl Spi1Instance for Spi1 {
193    const ID: Bank = Bank::Spi1;
194    const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi1;
195}
196impl Sealed for Spi1 {}
197
198#[cfg(feature = "vor1x")]
199pub type Spi2 = pac::Spic;
200#[cfg(feature = "vor4x")]
201pub type Spi2 = pac::Spi2;
202
203impl Spi2Instance for Spi2 {
204    const ID: Bank = Bank::Spi2;
205    const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi2;
206}
207impl Sealed for Spi2 {}
208
209#[cfg(feature = "vor4x")]
210impl Spi3Instance for pac::Spi3 {
211    const ID: Bank = Bank::Spi3;
212    const PERIPH_SEL: PeripheralSelect = PeripheralSelect::Spi3;
213}
214#[cfg(feature = "vor4x")]
215impl Sealed for pac::Spi3 {}
216
217//==================================================================================================
218// Config
219//==================================================================================================
220
221pub trait TransferConfigProvider {
222    fn sod(&mut self, sod: bool);
223    fn blockmode(&mut self, blockmode: bool);
224    fn mode(&mut self, mode: Mode);
225    fn clk_cfg(&mut self, clk_cfg: SpiClockConfig);
226    fn hw_cs_id(&self) -> u8;
227}
228
229/// Type erased variant of the transfer configuration. This is required to avoid generics in
230/// the SPI constructor.
231#[derive(Copy, Clone, Debug)]
232#[cfg_attr(feature = "defmt", derive(defmt::Format))]
233pub struct TransferConfig {
234    pub clk_cfg: Option<SpiClockConfig>,
235    pub mode: Option<Mode>,
236    pub sod: bool,
237    /// If this is enabled, all data in the FIFO is transmitted in a single frame unless
238    /// the BMSTOP bit is set on a dataword. A frame is defined as CSn being active for the
239    /// duration of multiple data words
240    pub blockmode: bool,
241    /// Only used when blockmode is used. The SCK will be stalled until an explicit stop bit
242    /// is set on a written word.
243    pub bmstall: bool,
244    pub hw_cs: Option<HwChipSelectId>,
245}
246
247impl TransferConfig {
248    pub fn new_with_hw_cs(
249        clk_cfg: Option<SpiClockConfig>,
250        mode: Option<Mode>,
251        blockmode: bool,
252        bmstall: bool,
253        sod: bool,
254        hw_cs_id: HwChipSelectId,
255    ) -> Self {
256        TransferConfig {
257            clk_cfg,
258            mode,
259            sod,
260            blockmode,
261            bmstall,
262            hw_cs: Some(hw_cs_id),
263        }
264    }
265}
266
267/// Configuration options for the whole SPI bus. See Programmer Guide p.92 for more details
268#[derive(Debug, Copy, Clone)]
269#[cfg_attr(feature = "defmt", derive(defmt::Format))]
270pub struct SpiConfig {
271    clk: SpiClockConfig,
272    // SPI mode configuration
273    pub init_mode: Mode,
274    /// If this is enabled, all data in the FIFO is transmitted in a single frame unless
275    /// the BMSTOP bit is set on a dataword. A frame is defined as CSn being active for the
276    /// duration of multiple data words. Defaults to true.
277    pub blockmode: bool,
278    /// This enables the stalling of the SPI SCK if in blockmode and the FIFO is empty.
279    /// Currently enabled by default.
280    pub bmstall: bool,
281    /// Slave output disable. Useful if separate GPIO pins or decoders are used for CS control
282    pub slave_output_disable: bool,
283    /// Loopback mode. If you use this, don't connect MISO to MOSI, they will be tied internally
284    pub loopback_mode: bool,
285    /// Enable Master Delayer Capture Mode. See Programmers Guide p.92 for more details
286    pub master_delayer_capture: bool,
287}
288
289impl Default for SpiConfig {
290    fn default() -> Self {
291        Self {
292            init_mode: MODE_0,
293            blockmode: true,
294            bmstall: true,
295            // Default value is definitely valid.
296            clk: SpiClockConfig::from_div(DEFAULT_CLK_DIV).unwrap(),
297            slave_output_disable: Default::default(),
298            loopback_mode: Default::default(),
299            master_delayer_capture: Default::default(),
300        }
301    }
302}
303
304impl SpiConfig {
305    pub fn loopback(mut self, enable: bool) -> Self {
306        self.loopback_mode = enable;
307        self
308    }
309
310    pub fn blockmode(mut self, enable: bool) -> Self {
311        self.blockmode = enable;
312        self
313    }
314
315    pub fn bmstall(mut self, enable: bool) -> Self {
316        self.bmstall = enable;
317        self
318    }
319
320    pub fn mode(mut self, mode: Mode) -> Self {
321        self.init_mode = mode;
322        self
323    }
324
325    pub fn clk_cfg(mut self, clk_cfg: SpiClockConfig) -> Self {
326        self.clk = clk_cfg;
327        self
328    }
329
330    pub fn slave_output_disable(mut self, sod: bool) -> Self {
331        self.slave_output_disable = sod;
332        self
333    }
334}
335
336//==================================================================================================
337// Word Size
338//==================================================================================================
339
340/// Configuration trait for the Word Size
341/// used by the SPI peripheral
342pub trait SpiWord: Copy + Default + Into<u32> + TryFrom<u32> + 'static {
343    const MASK: u32;
344    const WORD_SIZE: regs::WordSize;
345    fn word_reg() -> u8;
346}
347
348impl SpiWord for u8 {
349    const MASK: u32 = 0xff;
350    const WORD_SIZE: regs::WordSize = regs::WordSize::EightBits;
351    fn word_reg() -> u8 {
352        0x07
353    }
354}
355
356impl SpiWord for u16 {
357    const MASK: u32 = 0xffff;
358    const WORD_SIZE: regs::WordSize = regs::WordSize::SixteenBits;
359    fn word_reg() -> u8 {
360        0x0f
361    }
362}
363
364//==================================================================================================
365// Spi
366//==================================================================================================
367
368/// Low level access trait for the SPI peripheral.
369pub trait SpiLowLevel {
370    /// Low level function to write a word to the SPI FIFO but also checks whether
371    /// there is actually data in the FIFO.
372    ///
373    /// Uses the [nb] API to allow usage in blocking and non-blocking contexts.
374    fn write_fifo(&mut self, data: u32) -> nb::Result<(), Infallible>;
375
376    /// Low level function to write a word to the SPI FIFO without checking whether
377    /// there FIFO is full.
378    ///
379    /// This does not necesarily mean there is a space in the FIFO available.
380    /// Use [Self::write_fifo] function to write a word into the FIFO reliably.
381    fn write_fifo_unchecked(&mut self, data: u32);
382
383    /// Low level function to read a word from the SPI FIFO. Must be preceeded by a
384    /// [Self::write_fifo] call.
385    ///
386    /// Uses the [nb] API to allow usage in blocking and non-blocking contexts.
387    fn read_fifo(&mut self) -> nb::Result<u32, Infallible>;
388
389    /// Low level function to read a word from from the SPI FIFO.
390    ///
391    /// This does not necesarily mean there is a word in the FIFO available.
392    /// Use the [Self::read_fifo] function to read a word from the FIFO reliably using the [nb]
393    /// API.
394    /// You might also need to mask the value to ignore the BMSTART/BMSTOP bit.
395    fn read_fifo_unchecked(&mut self) -> u32;
396}
397
398#[inline(always)]
399pub fn mode_to_cpo_cph_bit(mode: embedded_hal::spi::Mode) -> (bool, bool) {
400    match mode {
401        embedded_hal::spi::MODE_0 => (false, false),
402        embedded_hal::spi::MODE_1 => (false, true),
403        embedded_hal::spi::MODE_2 => (true, false),
404        embedded_hal::spi::MODE_3 => (true, true),
405    }
406}
407
408#[derive(Debug, Copy, Clone, PartialEq, Eq)]
409#[cfg_attr(feature = "defmt", derive(defmt::Format))]
410pub struct SpiClockConfig {
411    prescale_val: u8,
412    scrdv: u8,
413}
414
415impl SpiClockConfig {
416    pub fn prescale_val(&self) -> u8 {
417        self.prescale_val
418    }
419    pub fn scrdv(&self) -> u8 {
420        self.scrdv
421    }
422}
423
424impl SpiClockConfig {
425    pub fn new(prescale_val: u8, scrdv: u8) -> Self {
426        Self {
427            prescale_val,
428            scrdv,
429        }
430    }
431
432    pub fn from_div(div: u16) -> Result<Self, SpiClockConfigError> {
433        spi_clk_config_from_div(div)
434    }
435
436    #[cfg(feature = "vor1x")]
437    pub fn from_clk(sys_clk: Hertz, spi_clk: Hertz) -> Option<Self> {
438        clk_div_for_target_clock(sys_clk, spi_clk).map(|div| spi_clk_config_from_div(div).unwrap())
439    }
440
441    #[cfg(feature = "vor4x")]
442    pub fn from_clks(clks: &crate::clock::Clocks, spi_clk: Hertz) -> Option<Self> {
443        Self::from_apb1_clk(clks.apb1(), spi_clk)
444    }
445
446    #[cfg(feature = "vor4x")]
447    pub fn from_apb1_clk(apb1_clk: Hertz, spi_clk: Hertz) -> Option<Self> {
448        clk_div_for_target_clock(apb1_clk, spi_clk).map(|div| spi_clk_config_from_div(div).unwrap())
449    }
450}
451
452#[derive(Debug, thiserror::Error)]
453#[cfg_attr(feature = "defmt", derive(defmt::Format))]
454pub enum SpiClockConfigError {
455    #[error("division by zero")]
456    DivIsZero,
457    #[error("divide value is not even")]
458    DivideValueNotEven,
459    #[error("scrdv value is too large")]
460    ScrdvValueTooLarge,
461}
462
463#[inline]
464pub fn spi_clk_config_from_div(mut div: u16) -> Result<SpiClockConfig, SpiClockConfigError> {
465    if div == 0 {
466        return Err(SpiClockConfigError::DivIsZero);
467    }
468    if !div.is_multiple_of(2) {
469        return Err(SpiClockConfigError::DivideValueNotEven);
470    }
471    let mut prescale_val = 0;
472
473    // find largest (even) prescale value that divides into div
474    for i in (2..=0xfe).rev().step_by(2) {
475        if div.is_multiple_of(i) {
476            prescale_val = i;
477            break;
478        }
479    }
480
481    if prescale_val == 0 {
482        return Err(SpiClockConfigError::DivideValueNotEven);
483    }
484
485    div /= prescale_val;
486    if div > u8::MAX as u16 + 1 {
487        return Err(SpiClockConfigError::ScrdvValueTooLarge);
488    }
489    Ok(SpiClockConfig {
490        prescale_val: prescale_val as u8,
491        scrdv: (div - 1) as u8,
492    })
493}
494
495#[inline]
496pub fn clk_div_for_target_clock(sys_clk: Hertz, spi_clk: Hertz) -> Option<u16> {
497    if spi_clk > sys_clk {
498        return None;
499    }
500
501    // Step 1: Calculate raw divider.
502    let raw_div = sys_clk.to_raw() / spi_clk.to_raw();
503    let remainder = sys_clk.to_raw() % spi_clk.to_raw();
504
505    // Step 2: Round up if necessary.
506    let mut rounded_div = if remainder * 2 >= spi_clk.to_raw() {
507        raw_div + 1
508    } else {
509        raw_div
510    };
511
512    if !rounded_div.is_multiple_of(2) {
513        // Take slower clock conservatively.
514        rounded_div += 1;
515    }
516    if rounded_div > u16::MAX as u32 {
517        return None;
518    }
519    Some(rounded_div as u16)
520}
521
522/// SPI peripheral driver structure.
523pub struct Spi<Word = u8> {
524    id: Bank,
525    regs: regs::MmioSpi<'static>,
526    /// Fill word for read-only SPI transactions.
527    fill_word: Word,
528    blockmode: bool,
529    bmstall: bool,
530    word: PhantomData<Word>,
531}
532
533impl<Word: SpiWord> Spi<Word>
534where
535    <Word as TryFrom<u32>>::Error: core::fmt::Debug,
536{
537    /// Create a new SPI struct for using SPI with the fixed ROM SPI pins.
538    ///
539    /// ## Arguments
540    ///
541    /// * `spi` - SPI bus to use
542    /// * `spi_cfg` - Configuration specific to the SPI bus
543    #[cfg(feature = "vor1x")]
544    pub fn new_for_rom<Spi: Spi2Instance>(_spi: Spi, spi_cfg: SpiConfig) -> Self {
545        Self::new_generic(Spi::ID, Spi::PERIPH_SEL, spi_cfg)
546    }
547
548    /// Create a new SPI struct for using SPI with the fixed ROM SPI pins.
549    ///
550    /// ## Arguments
551    ///
552    /// * `spi` - SPI bus to use
553    /// * `spi_cfg` - Configuration specific to the SPI bus
554    #[cfg(feature = "vor4x")]
555    pub fn new_for_rom<Spi: Spi3Instance>(_spi: Spi, spi_cfg: SpiConfig) -> Self {
556        Self::new_generic(Spi::ID, Spi::PERIPH_SEL, spi_cfg)
557    }
558
559    /// Create a new SPI peripheral driver for SPI 0.
560    ///
561    /// ## Arguments
562    ///
563    /// * `spi` - SPI bus to use
564    /// * `pins` - Pins to be used for SPI transactions. These pins are consumed
565    ///   to ensure the pins can not be used for other purposes anymore
566    /// * `spi_cfg` - Configuration specific to the SPI bus
567    pub fn new_for_spi0<Spi: Spi0Instance, Sck: PinSck0, Miso: PinMiso0, Mosi: PinMosi0>(
568        _spi: Spi,
569        _pins: (Sck, Miso, Mosi),
570        spi_cfg: SpiConfig,
571    ) -> Self {
572        IoPeriphPin::new(Sck::ID, Sck::FUN_SEL, None);
573        IoPeriphPin::new(Miso::ID, Miso::FUN_SEL, None);
574        IoPeriphPin::new(Mosi::ID, Mosi::FUN_SEL, None);
575        Self::new_generic(Spi::ID, Spi::PERIPH_SEL, spi_cfg)
576    }
577
578    /// Create a new SPI peripheral driver for SPI 1.
579    ///
580    /// ## Arguments
581    ///
582    /// * `spi` - SPI bus to use
583    /// * `pins` - Pins to be used for SPI transactions. These pins are consumed
584    ///   to ensure the pins can not be used for other purposes anymore
585    /// * `spi_cfg` - Configuration specific to the SPI bus
586    pub fn new_for_spi1<Spi: Spi1Instance, Sck: PinSck1, Miso: PinMiso1, Mosi: PinMosi1>(
587        _spi: Spi,
588        _pins: (Sck, Miso, Mosi),
589        spi_cfg: SpiConfig,
590    ) -> Self {
591        IoPeriphPin::new(Sck::ID, Sck::FUN_SEL, None);
592        IoPeriphPin::new(Miso::ID, Miso::FUN_SEL, None);
593        IoPeriphPin::new(Mosi::ID, Mosi::FUN_SEL, None);
594        Self::new_generic(Spi::ID, Spi::PERIPH_SEL, spi_cfg)
595    }
596
597    /// Create a new SPI peripheral driver for SPI 2.
598    ///
599    /// ## Arguments
600    ///
601    /// * `spi` - SPI bus to use
602    /// * `pins` - Pins to be used for SPI transactions. These pins are consumed
603    ///   to ensure the pins can not be used for other purposes anymore
604    /// * `spi_cfg` - Configuration specific to the SPI bus
605    pub fn new_for_spi2<Spi: Spi2Instance, Sck: PinSck2, Miso: PinMiso2, Mosi: PinMosi2>(
606        _spi: Spi,
607        _pins: (Sck, Miso, Mosi),
608        spi_cfg: SpiConfig,
609    ) -> Self {
610        IoPeriphPin::new(Sck::ID, Sck::FUN_SEL, None);
611        IoPeriphPin::new(Miso::ID, Miso::FUN_SEL, None);
612        IoPeriphPin::new(Mosi::ID, Mosi::FUN_SEL, None);
613        Self::new_generic(Spi::ID, Spi::PERIPH_SEL, spi_cfg)
614    }
615
616    pub fn new_generic(spi_sel: Bank, periph_sel: PeripheralSelect, spi_cfg: SpiConfig) -> Self {
617        enable_peripheral_clock(periph_sel);
618        let mut regs = regs::Spi::new_mmio(spi_sel);
619        let (cpo_bit, cph_bit) = mode_to_cpo_cph_bit(spi_cfg.init_mode);
620        regs.write_ctrl0(
621            regs::Control0::builder()
622                .with_scrdv(spi_cfg.clk.scrdv)
623                .with_sph(cph_bit)
624                .with_spo(cpo_bit)
625                .with_word_size(Word::WORD_SIZE)
626                .build(),
627        );
628        regs.write_ctrl1(
629            regs::Control1::builder()
630                .with_mtxpause(false)
631                .with_mdlycap(spi_cfg.master_delayer_capture)
632                .with_bm_stall(spi_cfg.bmstall)
633                .with_bm_start(false)
634                .with_blockmode(spi_cfg.blockmode)
635                .with_ss(HwChipSelectId::Id0)
636                .with_sod(spi_cfg.slave_output_disable)
637                .with_slave_mode(false)
638                .with_enable(false)
639                .with_lbm(spi_cfg.loopback_mode)
640                .build(),
641        );
642        regs.write_clkprescale(ClockPrescaler::new(spi_cfg.clk.prescale_val));
643        regs.write_fifo_clear(
644            FifoClear::builder()
645                .with_tx_fifo(true)
646                .with_rx_fifo(true)
647                .build(),
648        );
649        // Enable the peripheral as the last step as recommended in the
650        // programmers guide
651        regs.modify_ctrl1(|mut value| {
652            value.set_enable(true);
653            value
654        });
655        Spi {
656            id: spi_sel,
657            regs: regs::Spi::new_mmio(spi_sel),
658            fill_word: Default::default(),
659            bmstall: spi_cfg.bmstall,
660            blockmode: spi_cfg.blockmode,
661            word: PhantomData,
662        }
663    }
664
665    #[inline]
666    pub fn cfg_clock(&mut self, cfg: SpiClockConfig) {
667        self.regs.modify_ctrl0(|mut value| {
668            value.set_scrdv(cfg.scrdv);
669            value
670        });
671        self.regs
672            .write_clkprescale(regs::ClockPrescaler::new(cfg.prescale_val));
673    }
674
675    pub fn set_fill_word(&mut self, fill_word: Word) {
676        self.fill_word = fill_word;
677    }
678
679    #[inline]
680    pub fn configure_clock_from_div(&mut self, div: u16) -> Result<(), SpiClockConfigError> {
681        let val = spi_clk_config_from_div(div)?;
682        self.cfg_clock(val);
683        Ok(())
684    }
685
686    #[inline]
687    pub fn configure_mode(&mut self, mode: Mode) {
688        let (cpo_bit, cph_bit) = mode_to_cpo_cph_bit(mode);
689        self.regs.modify_ctrl0(|mut value| {
690            value.set_spo(cpo_bit);
691            value.set_sph(cph_bit);
692            value
693        });
694    }
695
696    #[inline]
697    pub fn fill_word(&self) -> Word {
698        self.fill_word
699    }
700
701    #[inline]
702    pub fn clear_tx_fifo(&mut self) {
703        self.regs.write_fifo_clear(
704            regs::FifoClear::builder()
705                .with_tx_fifo(true)
706                .with_rx_fifo(false)
707                .build(),
708        );
709    }
710
711    #[inline]
712    pub fn clear_rx_fifo(&mut self) {
713        self.regs.write_fifo_clear(
714            regs::FifoClear::builder()
715                .with_tx_fifo(false)
716                .with_rx_fifo(true)
717                .build(),
718        );
719    }
720
721    #[inline]
722    pub fn peripheral_id(&self) -> u32 {
723        self.regs.read_perid()
724    }
725
726    /// Configure the hardware chip select given a hardware chip select ID.
727    ///
728    /// The pin also needs to be configured to be used as a HW CS pin. This can be done
729    /// by using the [configure_pin_as_hw_cs_pin] function which also returns the
730    /// corresponding [HwChipSelectId].
731    #[inline]
732    pub fn configure_hw_cs(&mut self, hw_cs: HwChipSelectId) {
733        self.regs.modify_ctrl1(|mut value| {
734            value.set_sod(false);
735            value.set_ss(hw_cs);
736            value
737        });
738    }
739
740    /// Disables the hardware chip select functionality. This can be used when performing
741    /// external chip select handling, for example with GPIO pins.
742    #[inline]
743    pub fn disable_hw_cs(&mut self) {
744        self.regs.modify_ctrl1(|mut value| {
745            value.set_sod(true);
746            value
747        });
748    }
749
750    /// Utility function to configure all relevant transfer parameters in one go.
751    /// This is useful if multiple devices with different clock and mode configurations
752    /// are connected to one bus.
753    pub fn configure_transfer(&mut self, transfer_cfg: &TransferConfig) {
754        if let Some(trans_clk_div) = transfer_cfg.clk_cfg {
755            self.cfg_clock(trans_clk_div);
756        }
757        if let Some(mode) = transfer_cfg.mode {
758            self.configure_mode(mode);
759        }
760        self.blockmode = transfer_cfg.blockmode;
761        self.regs.modify_ctrl1(|mut value| {
762            if transfer_cfg.sod {
763                value.set_sod(transfer_cfg.sod);
764            } else {
765                value.set_sod(false);
766                if let Some(hw_cs) = transfer_cfg.hw_cs {
767                    value.set_ss(hw_cs);
768                }
769            }
770            value.set_blockmode(transfer_cfg.blockmode);
771            value.set_bm_stall(transfer_cfg.bmstall);
772            value
773        });
774    }
775
776    fn flush_internal(&mut self) {
777        let mut status_reg = self.regs.read_status();
778        while !status_reg.tx_empty() || status_reg.rx_not_empty() || status_reg.busy() {
779            if status_reg.rx_not_empty() {
780                self.read_fifo_unchecked();
781            }
782            status_reg = self.regs.read_status();
783        }
784    }
785
786    fn transfer_preparation(&mut self, words: &[Word]) {
787        if words.is_empty() {
788            return;
789        }
790        self.flush_internal();
791    }
792
793    // The FIFO can hold a guaranteed amount of data, so we can pump it on transfer
794    // initialization. Returns the amount of written bytes.
795    fn initial_send_fifo_pumping_with_words(&mut self, words: &[Word]) -> usize {
796        //let reg_block = self.reg_block();
797        if self.blockmode {
798            self.regs.modify_ctrl1(|mut value| {
799                value.set_mtxpause(true);
800                value
801            });
802        }
803        // Fill the first half of the write FIFO
804        let mut current_write_idx = 0;
805        let smaller_idx = core::cmp::min(FILL_DEPTH, words.len());
806        for _ in 0..smaller_idx {
807            if current_write_idx == smaller_idx.saturating_sub(1) && self.bmstall {
808                self.write_fifo_unchecked(words[current_write_idx].into() | BMSTART_BMSTOP_MASK);
809            } else {
810                self.write_fifo_unchecked(words[current_write_idx].into());
811            }
812            current_write_idx += 1;
813        }
814        if self.blockmode {
815            self.regs.modify_ctrl1(|mut value| {
816                value.set_mtxpause(false);
817                value
818            });
819        }
820        current_write_idx
821    }
822
823    // The FIFO can hold a guaranteed amount of data, so we can pump it on transfer
824    // initialization.
825    fn initial_send_fifo_pumping_with_fill_words(&mut self, send_len: usize) -> usize {
826        if self.blockmode {
827            self.regs.modify_ctrl1(|mut value| {
828                value.set_mtxpause(true);
829                value
830            });
831        }
832        // Fill the first half of the write FIFO
833        let mut current_write_idx = 0;
834        let smaller_idx = core::cmp::min(FILL_DEPTH, send_len);
835        for _ in 0..smaller_idx {
836            if current_write_idx == smaller_idx.saturating_sub(1) && self.bmstall {
837                self.write_fifo_unchecked(self.fill_word.into() | BMSTART_BMSTOP_MASK);
838            } else {
839                self.write_fifo_unchecked(self.fill_word.into());
840            }
841            current_write_idx += 1;
842        }
843        if self.blockmode {
844            self.regs.modify_ctrl1(|mut value| {
845                value.set_mtxpause(false);
846                value
847            });
848        }
849        current_write_idx
850    }
851
852    pub fn read(&mut self, words: &mut [Word]) {
853        self.transfer_preparation(words);
854        let mut current_read_idx = 0;
855        let mut current_write_idx = self.initial_send_fifo_pumping_with_fill_words(words.len());
856        loop {
857            if current_read_idx < words.len() {
858                words[current_read_idx] = (nb::block!(self.read_fifo()).unwrap() & Word::MASK)
859                    .try_into()
860                    .unwrap();
861                current_read_idx += 1;
862            }
863            if current_write_idx < words.len() {
864                if current_write_idx == words.len() - 1 && self.bmstall {
865                    nb::block!(self.write_fifo(self.fill_word.into() | BMSTART_BMSTOP_MASK))
866                        .unwrap();
867                } else {
868                    nb::block!(self.write_fifo(self.fill_word.into())).unwrap();
869                }
870                current_write_idx += 1;
871            }
872            if current_read_idx >= words.len() && current_write_idx >= words.len() {
873                break;
874            }
875        }
876    }
877
878    pub fn write(&mut self, words: &[Word]) {
879        self.transfer_preparation(words);
880        let mut current_write_idx = self.initial_send_fifo_pumping_with_words(words);
881        while current_write_idx < words.len() {
882            if current_write_idx == words.len() - 1 && self.bmstall {
883                nb::block!(self.write_fifo(words[current_write_idx].into() | BMSTART_BMSTOP_MASK))
884                    .unwrap();
885            } else {
886                nb::block!(self.write_fifo(words[current_write_idx].into())).unwrap();
887            }
888            current_write_idx += 1;
889            // Ignore received words.
890            if self.regs.read_status().rx_not_empty() {
891                self.clear_rx_fifo();
892            }
893        }
894    }
895
896    pub fn transfer(&mut self, read: &mut [Word], write: &[Word]) {
897        self.transfer_preparation(write);
898        let mut current_read_idx = 0;
899        let mut current_write_idx = self.initial_send_fifo_pumping_with_words(write);
900        let max_idx = core::cmp::max(read.len(), write.len());
901        while current_read_idx < read.len() || current_write_idx < write.len() {
902            if current_write_idx < max_idx {
903                if current_write_idx == write.len() - 1 && self.bmstall {
904                    nb::block!(
905                        self.write_fifo(write[current_write_idx].into() | BMSTART_BMSTOP_MASK)
906                    )
907                    .unwrap();
908                } else if current_write_idx < write.len() {
909                    nb::block!(self.write_fifo(write[current_write_idx].into())).unwrap();
910                } else {
911                    nb::block!(self.write_fifo(0)).unwrap();
912                }
913                current_write_idx += 1;
914            }
915            if current_read_idx < max_idx {
916                if current_read_idx < read.len() {
917                    read[current_read_idx] = (nb::block!(self.read_fifo()).unwrap() & Word::MASK)
918                        .try_into()
919                        .unwrap();
920                } else {
921                    nb::block!(self.read_fifo()).unwrap();
922                }
923                current_read_idx += 1;
924            }
925        }
926    }
927
928    pub fn transfer_in_place(&mut self, words: &mut [Word]) {
929        self.transfer_preparation(words);
930        let mut current_read_idx = 0;
931        let mut current_write_idx = self.initial_send_fifo_pumping_with_words(words);
932
933        while current_read_idx < words.len() || current_write_idx < words.len() {
934            if current_write_idx < words.len() {
935                if current_write_idx == words.len() - 1 && self.bmstall {
936                    nb::block!(
937                        self.write_fifo(words[current_write_idx].into() | BMSTART_BMSTOP_MASK)
938                    )
939                    .unwrap();
940                } else {
941                    nb::block!(self.write_fifo(words[current_write_idx].into())).unwrap();
942                }
943                current_write_idx += 1;
944            }
945            if current_read_idx < words.len() && current_read_idx < current_write_idx {
946                words[current_read_idx] = (nb::block!(self.read_fifo()).unwrap() & Word::MASK)
947                    .try_into()
948                    .unwrap();
949                current_read_idx += 1;
950            }
951        }
952    }
953
954    pub fn flush(&mut self) {
955        self.flush_internal();
956    }
957}
958
959impl<W: SpiWord> SpiLowLevel for Spi<W>
960where
961    <W as TryFrom<u32>>::Error: core::fmt::Debug,
962{
963    #[inline(always)]
964    fn write_fifo(&mut self, data: u32) -> nb::Result<(), Infallible> {
965        if !self.regs.read_status().tx_not_full() {
966            return Err(nb::Error::WouldBlock);
967        }
968        self.write_fifo_unchecked(data);
969        Ok(())
970    }
971
972    #[inline(always)]
973    fn write_fifo_unchecked(&mut self, data: u32) {
974        self.regs.write_data(Data::new_with_raw_value(data));
975    }
976
977    #[inline(always)]
978    fn read_fifo(&mut self) -> nb::Result<u32, Infallible> {
979        if !self.regs.read_status().rx_not_empty() {
980            return Err(nb::Error::WouldBlock);
981        }
982        Ok(self.read_fifo_unchecked())
983    }
984
985    #[inline(always)]
986    fn read_fifo_unchecked(&mut self) -> u32 {
987        self.regs.read_data().raw_value()
988    }
989}
990
991impl<Word: SpiWord> embedded_hal::spi::ErrorType for Spi<Word> {
992    type Error = Infallible;
993}
994
995impl<Word: SpiWord> embedded_hal::spi::SpiBus<Word> for Spi<Word>
996where
997    <Word as TryFrom<u32>>::Error: core::fmt::Debug,
998{
999    fn read(&mut self, words: &mut [Word]) -> Result<(), Self::Error> {
1000        self.read(words);
1001        Ok(())
1002    }
1003
1004    fn write(&mut self, words: &[Word]) -> Result<(), Self::Error> {
1005        self.write(words);
1006        Ok(())
1007    }
1008
1009    fn transfer(&mut self, read: &mut [Word], write: &[Word]) -> Result<(), Self::Error> {
1010        self.transfer(read, write);
1011        Ok(())
1012    }
1013
1014    fn transfer_in_place(&mut self, words: &mut [Word]) -> Result<(), Self::Error> {
1015        self.transfer_in_place(words);
1016        Ok(())
1017    }
1018
1019    fn flush(&mut self) -> Result<(), Self::Error> {
1020        self.flush();
1021        Ok(())
1022    }
1023}
1024
1025/// Changing the word size also requires a type conversion
1026impl From<Spi<u8>> for Spi<u16> {
1027    fn from(mut old_spi: Spi<u8>) -> Self {
1028        old_spi.regs.modify_ctrl0(|mut value| {
1029            value.set_word_size(WordSize::SixteenBits);
1030            value
1031        });
1032        Spi {
1033            id: old_spi.id,
1034            regs: old_spi.regs,
1035            blockmode: old_spi.blockmode,
1036            fill_word: Default::default(),
1037            bmstall: old_spi.bmstall,
1038            word: PhantomData,
1039        }
1040    }
1041}
1042
1043impl From<Spi<u16>> for Spi<u8> {
1044    fn from(mut old_spi: Spi<u16>) -> Self {
1045        old_spi.regs.modify_ctrl0(|mut value| {
1046            value.set_word_size(WordSize::EightBits);
1047            value
1048        });
1049        Spi {
1050            id: old_spi.id,
1051            regs: old_spi.regs,
1052            blockmode: old_spi.blockmode,
1053            fill_word: Default::default(),
1054            bmstall: old_spi.bmstall,
1055            word: PhantomData,
1056        }
1057    }
1058}
1059
1060/// This abstraction which can be used to map a hardware chip select pin
1061/// to [embedded_hal::digital::OutputPin]. This is useful for creating physical chip select
1062/// pins required by the [embedded_hal_bus](https://docs.rs/embedded-hal-bus/latest/embedded_hal_bus/)
1063/// API.
1064pub struct HwCsPin {
1065    regs: regs::MmioSpi<'static>,
1066    id: HwChipSelectId,
1067}
1068
1069impl HwCsPin {
1070    pub fn new<P: HwCsProvider + AnyPin>(pin: P) -> Self {
1071        configure_pin_as_hw_cs_pin(pin);
1072        Self {
1073            regs: unsafe { P::SPI_ID.steal_regs() },
1074            id: P::CS_ID,
1075        }
1076    }
1077}
1078
1079impl embedded_hal::digital::ErrorType for HwCsPin {
1080    type Error = Infallible;
1081}
1082
1083impl embedded_hal::digital::OutputPin for HwCsPin {
1084    fn set_low(&mut self) -> Result<(), Self::Error> {
1085        self.regs
1086            .modify_ctrl1(|value| value.with_sod(false).with_ss(self.id));
1087        Ok(())
1088    }
1089
1090    fn set_high(&mut self) -> Result<(), Self::Error> {
1091        self.regs.modify_ctrl1(|value| value.with_sod(true));
1092        Ok(())
1093    }
1094}