zynq7000_hal/qspi/
mod.rs

1//! # QSPI module
2//!
3//! ## Examples
4//!
5//! - [Zedboard QSPI](https://egit.irs.uni-stuttgart.de/rust/zynq7000-rs/src/branch/main/zynq/examples/zedboard/src/bin/qspi.rs)
6use core::ops::{Deref, DerefMut};
7
8use arbitrary_int::{prelude::*, u2, u3, u6};
9pub use zynq7000::qspi::LinearQspiConfig;
10use zynq7000::{
11    qspi::{
12        BaudRateDivisor, Config, InstructionCode, InterruptStatus, LoopbackMasterClockDelay,
13        SpiEnable,
14    },
15    slcr::{clocks::SingleCommonPeriphIoClockControl, mio::Speed, reset::QspiResetControl},
16};
17
18pub use embedded_hal::spi::{MODE_0, MODE_1, MODE_2, MODE_3, Mode};
19pub use zynq7000::slcr::clocks::SrcSelIo;
20pub use zynq7000::slcr::mio::IoType;
21
22use crate::{
23    PeriphSelect,
24    clocks::Clocks,
25    enable_amba_peripheral_clock,
26    gpio::{
27        IoPeriphPin,
28        mio::{
29            Mio0, Mio1, Mio2, Mio3, Mio4, Mio5, Mio6, Mio8, Mio9, Mio10, Mio11, Mio12, Mio13,
30            MioPin, MuxConfig, Pin,
31        },
32    },
33    slcr::Slcr,
34    spi_mode_const_to_cpol_cpha,
35    time::Hertz,
36};
37
38pub(crate) mod lqspi_configs;
39
40pub const QSPI_MUX_CONFIG: MuxConfig = MuxConfig::new_with_l0();
41pub const FIFO_DEPTH: usize = 63;
42/// In linear-addressed mode, the QSPI is memory-mapped, with the address starting here.
43pub const QSPI_START_ADDRESS: usize = 0xFC00_0000;
44
45#[derive(Debug, thiserror::Error)]
46pub enum ClockCalculationError {
47    #[error("violated clock ratio restriction")]
48    RefClockSmallerThanCpu1xClock,
49    #[error("reference divisor out of range")]
50    RefDivOutOfRange,
51}
52
53#[derive(Debug, Clone, Copy, PartialEq, Eq)]
54pub enum BaudRateConfig {
55    WithLoopback,
56    WithoutLoopback(BaudRateDivisor),
57}
58
59impl BaudRateConfig {
60    #[inline]
61    pub const fn baud_rate_divisor(&self) -> BaudRateDivisor {
62        match self {
63            BaudRateConfig::WithLoopback => BaudRateDivisor::_2,
64            BaudRateConfig::WithoutLoopback(divisor) => *divisor,
65        }
66    }
67}
68
69#[derive(Debug, Clone, Copy, PartialEq, Eq)]
70pub struct ClockConfig {
71    pub src_sel: SrcSelIo,
72    pub ref_clk_div: u6,
73    pub baud_rate_config: BaudRateConfig,
74}
75
76#[derive(Debug, Clone, Copy, PartialEq, Eq)]
77pub enum QspiVendor {
78    WinbondAndSpansion,
79    Micron,
80}
81
82pub type OperatingMode = InstructionCode;
83
84pub trait Qspi0ChipSelectPin: MioPin {}
85pub trait Qspi0Io0Pin: MioPin {}
86pub trait Qspi0Io1Pin: MioPin {}
87pub trait Qspi0Io2Pin: MioPin {}
88pub trait Qspi0Io3Pin: MioPin {}
89pub trait Qspi0ClockPin: MioPin {}
90
91impl Qspi0ChipSelectPin for Pin<Mio1> {}
92impl Qspi0Io0Pin for Pin<Mio2> {}
93impl Qspi0Io1Pin for Pin<Mio3> {}
94impl Qspi0Io2Pin for Pin<Mio4> {}
95impl Qspi0Io3Pin for Pin<Mio5> {}
96impl Qspi0ClockPin for Pin<Mio6> {}
97
98pub trait Qspi1ChipSelectPin: MioPin {}
99pub trait Qspi1Io0Pin: MioPin {}
100pub trait Qspi1Io1Pin: MioPin {}
101pub trait Qspi1Io2Pin: MioPin {}
102pub trait Qspi1Io3Pin: MioPin {}
103pub trait Qspi1ClockPin: MioPin {}
104
105impl Qspi1ChipSelectPin for Pin<Mio0> {}
106impl Qspi1Io0Pin for Pin<Mio10> {}
107impl Qspi1Io1Pin for Pin<Mio11> {}
108impl Qspi1Io2Pin for Pin<Mio12> {}
109impl Qspi1Io3Pin for Pin<Mio13> {}
110impl Qspi1ClockPin for Pin<Mio9> {}
111
112pub trait FeedbackClockPin: MioPin {}
113
114impl FeedbackClockPin for Pin<Mio8> {}
115
116pub struct QspiDeviceCombination {
117    pub vendor: QspiVendor,
118    pub operating_mode: OperatingMode,
119    pub two_devices: bool,
120}
121
122impl From<QspiDeviceCombination> for LinearQspiConfig {
123    fn from(value: QspiDeviceCombination) -> Self {
124        linear_mode_config_for_common_devices(value)
125    }
126}
127
128pub const fn linear_mode_config_for_common_devices(
129    dev_combination: QspiDeviceCombination,
130) -> LinearQspiConfig {
131    match dev_combination.operating_mode {
132        InstructionCode::Read => {
133            if dev_combination.two_devices {
134                lqspi_configs::RD_TWO
135            } else {
136                lqspi_configs::RD_ONE
137            }
138        }
139        InstructionCode::FastRead => {
140            if dev_combination.two_devices {
141                lqspi_configs::FAST_RD_TWO
142            } else {
143                lqspi_configs::FAST_RD_ONE
144            }
145        }
146        InstructionCode::FastReadDualOutput => {
147            if dev_combination.two_devices {
148                lqspi_configs::DUAL_OUT_FAST_RD_TWO
149            } else {
150                lqspi_configs::DUAL_OUT_FAST_RD_ONE
151            }
152        }
153        InstructionCode::FastReadQuadOutput => {
154            if dev_combination.two_devices {
155                lqspi_configs::QUAD_OUT_FAST_RD_TWO
156            } else {
157                lqspi_configs::QUAD_OUT_FAST_RD_ONE
158            }
159        }
160        InstructionCode::FastReadDualIo => {
161            match (dev_combination.vendor, dev_combination.two_devices) {
162                (QspiVendor::WinbondAndSpansion, false) => {
163                    lqspi_configs::winbond_spansion::DUAL_IO_FAST_RD_ONE
164                }
165                (QspiVendor::WinbondAndSpansion, true) => {
166                    lqspi_configs::winbond_spansion::DUAL_IO_FAST_RD_TWO
167                }
168                (QspiVendor::Micron, false) => lqspi_configs::micron::DUAL_IO_FAST_RD_ONE,
169                (QspiVendor::Micron, true) => lqspi_configs::micron::DUAL_IO_FAST_RD_TWO,
170            }
171        }
172        InstructionCode::FastReadQuadIo => {
173            match (dev_combination.vendor, dev_combination.two_devices) {
174                (QspiVendor::WinbondAndSpansion, false) => {
175                    lqspi_configs::winbond_spansion::QUAD_IO_FAST_RD_ONE
176                }
177                (QspiVendor::WinbondAndSpansion, true) => {
178                    lqspi_configs::winbond_spansion::QUAD_IO_FAST_RD_TWO
179                }
180                (QspiVendor::Micron, false) => lqspi_configs::micron::QUAD_IO_FAST_RD_ONE,
181                (QspiVendor::Micron, true) => lqspi_configs::micron::QUAD_IO_FAST_RD_TWO,
182            }
183        }
184    }
185}
186
187impl ClockConfig {
188    pub fn new(src_sel: SrcSelIo, ref_clk_div: u6, baud_rate_config: BaudRateConfig) -> Self {
189        Self {
190            src_sel,
191            ref_clk_div,
192            baud_rate_config,
193        }
194    }
195
196    /// This constructor calculates the necessary clock divisor for a target QSPI reference clock,
197    /// assuming that a loopback clock is used and thus constraining the baud rate divisor to 2.
198    ///
199    /// It also checks that the clock ratio restriction is not violated: The QSPI reference clock must
200    /// be greater than the CPU 1x clock.
201    pub fn calculate_with_loopback(
202        src_sel: SrcSelIo,
203        clocks: &Clocks,
204        target_qspi_interface_clock: Hertz,
205    ) -> Result<Self, ClockCalculationError> {
206        // For loopback mode, the baud rate divisor MUST be 2.
207        let target_ref_clock = target_qspi_interface_clock * 2;
208        let ref_clk = match src_sel {
209            SrcSelIo::IoPll | SrcSelIo::IoPllAlt => clocks.io_clocks().ref_clk(),
210            SrcSelIo::ArmPll => clocks.arm_clocks().ref_clk(),
211            SrcSelIo::DdrPll => clocks.ddr_clocks().ref_clk(),
212        };
213        let ref_clk_div = ref_clk.raw().div_ceil(target_ref_clock.raw());
214        if ref_clk_div > u6::MAX.as_u32() {
215            return Err(ClockCalculationError::RefDivOutOfRange);
216        }
217        Ok(Self {
218            src_sel,
219            ref_clk_div: u6::new(ref_clk_div as u8),
220            baud_rate_config: BaudRateConfig::WithLoopback,
221        })
222    }
223
224    /// This constructor calculates the necessary clock configuration for both a target QSPI
225    /// reference clock as well as a target QSPI interface clock.
226    ///
227    /// It also checks that the clock ratio restriction is not violated: The QSPI reference clock must
228    /// be greater than the CPU 1x clock.
229    pub fn calculate(
230        src_sel: SrcSelIo,
231        clocks: &Clocks,
232        target_qspi_ref_clock: Hertz,
233        target_qspi_interface_clock: Hertz,
234    ) -> Result<Self, ClockCalculationError> {
235        let (ref_clk_div, ref_clk) = match src_sel {
236            SrcSelIo::IoPll | SrcSelIo::IoPllAlt => (
237                clocks
238                    .io_clocks()
239                    .ref_clk()
240                    .raw()
241                    .div_ceil(target_qspi_ref_clock.raw()),
242                clocks.io_clocks().ref_clk(),
243            ),
244            SrcSelIo::ArmPll => (
245                clocks
246                    .arm_clocks()
247                    .ref_clk()
248                    .raw()
249                    .div_ceil(target_qspi_ref_clock.raw()),
250                clocks.arm_clocks().ref_clk(),
251            ),
252            SrcSelIo::DdrPll => (
253                clocks
254                    .ddr_clocks()
255                    .ref_clk()
256                    .raw()
257                    .div_ceil(target_qspi_ref_clock.raw()),
258                clocks.ddr_clocks().ref_clk(),
259            ),
260        };
261        if ref_clk_div > u6::MAX.as_u32() {
262            return Err(ClockCalculationError::RefDivOutOfRange);
263        }
264        let qspi_ref_clk = ref_clk / ref_clk_div;
265        if qspi_ref_clk < clocks.arm_clocks().cpu_1x_clk() {
266            return Err(ClockCalculationError::RefClockSmallerThanCpu1xClock);
267        }
268        let qspi_baud_rate_div = qspi_ref_clk / target_qspi_interface_clock;
269        let baud_rate_div = match qspi_baud_rate_div {
270            0..=2 => BaudRateDivisor::_2,
271            3..=4 => BaudRateDivisor::_4,
272            5..=8 => BaudRateDivisor::_8,
273            9..=16 => BaudRateDivisor::_16,
274            17..=32 => BaudRateDivisor::_32,
275            65..=128 => BaudRateDivisor::_64,
276            129..=256 => BaudRateDivisor::_128,
277            _ => BaudRateDivisor::_256,
278        };
279        Ok(Self {
280            src_sel,
281            ref_clk_div: u6::new(ref_clk_div as u8),
282            baud_rate_config: BaudRateConfig::WithoutLoopback(baud_rate_div),
283        })
284    }
285}
286
287pub struct QspiLowLevel(zynq7000::qspi::MmioQspi<'static>);
288
289impl QspiLowLevel {
290    #[inline]
291    pub fn new(regs: zynq7000::qspi::MmioQspi<'static>) -> Self {
292        Self(regs)
293    }
294
295    pub fn regs(&mut self) -> &mut zynq7000::qspi::MmioQspi<'static> {
296        &mut self.0
297    }
298
299    pub fn initialize(&mut self, clock_config: ClockConfig, mode: embedded_hal::spi::Mode) {
300        enable_amba_peripheral_clock(PeriphSelect::Lqspi);
301        reset();
302        let (cpol, cpha) = spi_mode_const_to_cpol_cpha(mode);
303        unsafe {
304            Slcr::with(|slcr| {
305                slcr.clk_ctrl().write_lqspi_clk_ctrl(
306                    SingleCommonPeriphIoClockControl::builder()
307                        .with_divisor(clock_config.ref_clk_div)
308                        .with_srcsel(clock_config.src_sel)
309                        .with_clk_act(true)
310                        .build(),
311                );
312            })
313        }
314        let baudrate_config = clock_config.baud_rate_config;
315        self.0.write_config(
316            Config::builder()
317                .with_interface_mode(zynq7000::qspi::InterfaceMode::FlashMemoryInterface)
318                .with_edianness(zynq7000::qspi::Endianness::Little)
319                .with_holdb_dr(true)
320                .with_manual_start_command(false)
321                .with_manual_start_enable(false)
322                .with_manual_cs(false)
323                .with_peripheral_chip_select(false)
324                .with_fifo_width(u2::new(0b11))
325                .with_baud_rate_div(baudrate_config.baud_rate_divisor())
326                .with_clock_phase(cpha)
327                .with_clock_polarity(cpol)
328                .with_mode_select(true)
329                .build(),
330        );
331        if baudrate_config == BaudRateConfig::WithLoopback {
332            self.0.write_loopback_master_clock_delay(
333                LoopbackMasterClockDelay::builder()
334                    .with_use_loopback(true)
335                    .with_delay_1(u2::new(0x0))
336                    .with_delay_0(u3::new(0x0))
337                    .build(),
338            );
339        }
340    }
341
342    pub fn enable_linear_addressing(&mut self, config: LinearQspiConfig) {
343        self.0
344            .write_spi_enable(SpiEnable::builder().with_enable(false).build());
345        self.0.modify_config(|mut val| {
346            // Those two bits should be set to 0 according to the TRM.
347            val.set_manual_start_enable(false);
348            val.set_manual_cs(false);
349            val.set_peripheral_chip_select(false);
350            val
351        });
352        self.0.write_linear_qspi_config(config);
353    }
354
355    pub fn enable_io_mode(&mut self, dual_flash: bool) {
356        self.0.modify_config(|mut val| {
357            val.set_manual_start_enable(true);
358            val.set_manual_cs(true);
359            val
360        });
361        self.0.write_rx_fifo_threshold(0x1);
362        self.0.write_tx_fifo_threshold(0x1);
363        self.0.write_linear_qspi_config(
364            LinearQspiConfig::builder()
365                .with_enable_linear_mode(false)
366                .with_both_memories(dual_flash)
367                .with_separate_memory_bus(dual_flash)
368                .with_upper_memory_page(false)
369                .with_mode_enable(false)
370                .with_mode_on(true)
371                // Reset values from the TRM are set here, but they do not matter anyway.
372                .with_mode_bits(0xA0)
373                .with_num_dummy_bytes(u3::new(0x2))
374                .with_instruction_code(InstructionCode::FastReadQuadIo)
375                .build(),
376        );
377    }
378
379    pub fn disable(&mut self) {
380        self.0
381            .write_spi_enable(SpiEnable::builder().with_enable(false).build());
382        self.0.modify_config(|mut val| {
383            val.set_peripheral_chip_select(true);
384            val
385        });
386    }
387}
388
389pub struct Qspi {
390    ll: QspiLowLevel,
391}
392
393impl Qspi {
394    pub fn new_single_qspi<
395        ChipSelect: Qspi0ChipSelectPin,
396        Io0: Qspi0Io0Pin,
397        Io1: Qspi0Io1Pin,
398        Io2: Qspi0Io2Pin,
399        Io3: Qspi0Io3Pin,
400        Clock: Qspi0ClockPin,
401    >(
402        regs: zynq7000::qspi::MmioQspi<'static>,
403        clock_config: ClockConfig,
404        mode: embedded_hal::spi::Mode,
405        voltage: IoType,
406        cs: ChipSelect,
407        ios: (Io0, Io1, Io2, Io3),
408        clock: Clock,
409    ) -> Self {
410        IoPeriphPin::new_with_full_config(
411            cs,
412            zynq7000::slcr::mio::Config::builder()
413                .with_disable_hstl_rcvr(false)
414                .with_pullup(true)
415                .with_io_type(voltage)
416                .with_speed(Speed::SlowCmosEdge)
417                .with_l3_sel(QSPI_MUX_CONFIG.l3_sel())
418                .with_l2_sel(QSPI_MUX_CONFIG.l2_sel())
419                .with_l1_sel(QSPI_MUX_CONFIG.l1_sel())
420                .with_l0_sel(QSPI_MUX_CONFIG.l0_sel())
421                .with_tri_enable(false)
422                .build(),
423        );
424        let io_and_clock_config = zynq7000::slcr::mio::Config::builder()
425            .with_disable_hstl_rcvr(false)
426            .with_pullup(false)
427            .with_io_type(voltage)
428            .with_speed(Speed::SlowCmosEdge)
429            .with_l3_sel(QSPI_MUX_CONFIG.l3_sel())
430            .with_l2_sel(QSPI_MUX_CONFIG.l2_sel())
431            .with_l1_sel(QSPI_MUX_CONFIG.l1_sel())
432            .with_l0_sel(QSPI_MUX_CONFIG.l0_sel())
433            .with_tri_enable(false)
434            .build();
435        IoPeriphPin::new_with_full_config(ios.0, io_and_clock_config);
436        IoPeriphPin::new_with_full_config(ios.1, io_and_clock_config);
437        IoPeriphPin::new_with_full_config(ios.2, io_and_clock_config);
438        IoPeriphPin::new_with_full_config(ios.3, io_and_clock_config);
439        IoPeriphPin::new_with_full_config(clock, io_and_clock_config);
440        let mut ll = QspiLowLevel::new(regs);
441        ll.initialize(clock_config, mode);
442        Self { ll }
443    }
444
445    #[allow(clippy::too_many_arguments)]
446    pub fn new_single_qspi_with_feedback<
447        ChipSelect: Qspi0ChipSelectPin,
448        Io0: Qspi0Io0Pin,
449        Io1: Qspi0Io1Pin,
450        Io2: Qspi0Io2Pin,
451        Io3: Qspi0Io3Pin,
452        Clock: Qspi0ClockPin,
453        Feedback: FeedbackClockPin,
454    >(
455        regs: zynq7000::qspi::MmioQspi<'static>,
456        clock_config: ClockConfig,
457        mode: embedded_hal::spi::Mode,
458        voltage: IoType,
459        cs: ChipSelect,
460        io: (Io0, Io1, Io2, Io3),
461        clock: Clock,
462        feedback: Feedback,
463    ) -> Self {
464        IoPeriphPin::new_with_full_config(
465            feedback,
466            zynq7000::slcr::mio::Config::builder()
467                .with_disable_hstl_rcvr(false)
468                .with_pullup(false)
469                .with_io_type(voltage)
470                .with_speed(Speed::SlowCmosEdge)
471                .with_l3_sel(QSPI_MUX_CONFIG.l3_sel())
472                .with_l2_sel(QSPI_MUX_CONFIG.l2_sel())
473                .with_l1_sel(QSPI_MUX_CONFIG.l1_sel())
474                .with_l0_sel(QSPI_MUX_CONFIG.l0_sel())
475                .with_tri_enable(false)
476                .build(),
477        );
478        Self::new_single_qspi(regs, clock_config, mode, voltage, cs, io, clock)
479    }
480
481    #[inline]
482    pub fn regs(&mut self) -> &mut zynq7000::qspi::MmioQspi<'static> {
483        &mut self.ll.0
484    }
485
486    pub fn into_linear_addressed(mut self, config: LinearQspiConfig) -> QspiLinearAddressing {
487        self.ll.enable_linear_addressing(config);
488        QspiLinearAddressing { ll: self.ll }
489    }
490
491    pub fn into_io_mode(mut self, dual_flash: bool) -> QspiIoMode {
492        self.ll.enable_io_mode(dual_flash);
493        QspiIoMode { ll: self.ll }
494    }
495}
496
497pub struct QspiIoMode {
498    ll: QspiLowLevel,
499}
500
501impl QspiIoMode {
502    #[inline]
503    pub fn regs(&mut self) -> &mut zynq7000::qspi::MmioQspi<'static> {
504        &mut self.ll.0
505    }
506
507    pub fn transfer_guard(&mut self) -> QspiIoTransferGuard<'_> {
508        QspiIoTransferGuard::new(self)
509    }
510
511    pub fn into_qspi(mut self, ll: QspiLowLevel) -> Qspi {
512        self.ll.disable();
513        Qspi { ll }
514    }
515
516    /// Transmits 1-byte command and 3-byte data OR 4-byte data.
517    #[inline]
518    pub fn write_word_txd_00(&mut self, word: u32) {
519        self.regs().write_tx_data_00(word);
520    }
521
522    /// Transmits 1-byte command.
523    #[inline]
524    pub fn write_word_txd_01(&mut self, word: u32) {
525        self.regs().write_tx_data_01(word);
526    }
527
528    /// Transmits 1-byte command and 1-byte data.
529    #[inline]
530    pub fn write_word_txd_10(&mut self, word: u32) {
531        self.regs().write_tx_data_10(word);
532    }
533
534    /// Transmits 1-byte command and 2-byte data.
535    #[inline]
536    pub fn write_word_txd_11(&mut self, word: u32) {
537        self.regs().write_tx_data_11(word);
538    }
539
540    #[inline]
541    pub fn read_rx_data(&mut self) -> u32 {
542        self.regs().read_rx_data()
543    }
544
545    pub fn transfer_init(&mut self) {
546        self.regs().modify_config(|mut val| {
547            val.set_peripheral_chip_select(false);
548            val
549        });
550        self.regs()
551            .write_spi_enable(SpiEnable::builder().with_enable(true).build());
552    }
553
554    pub fn transfer_start(&mut self) {
555        self.regs().modify_config(|mut val| {
556            val.set_manual_start_command(true);
557            val
558        });
559    }
560
561    pub fn transfer_done(&mut self) {
562        self.regs().modify_config(|mut val| {
563            val.set_peripheral_chip_select(true);
564            val
565        });
566        self.regs()
567            .write_spi_enable(SpiEnable::builder().with_enable(false).build());
568    }
569
570    pub fn read_status(&mut self) -> InterruptStatus {
571        self.regs().read_interrupt_status()
572    }
573
574    pub fn clear_rx_fifo(&mut self) {
575        while self.read_status().rx_above_threshold() {
576            self.read_rx_data();
577        }
578    }
579
580    pub fn into_linear_addressed(mut self, config: LinearQspiConfig) -> QspiLinearAddressing {
581        self.ll.enable_linear_addressing(config);
582        QspiLinearAddressing { ll: self.ll }
583    }
584}
585
586/// This guard structure takes care of commonly required operations before starting a transfer
587/// and after finishing it.
588pub struct QspiIoTransferGuard<'a>(&'a mut QspiIoMode);
589
590impl<'a> QspiIoTransferGuard<'a> {
591    pub fn new(qspi: &'a mut QspiIoMode) -> Self {
592        qspi.clear_rx_fifo();
593        qspi.transfer_init();
594        Self(qspi)
595    }
596}
597
598impl QspiIoTransferGuard<'_> {
599    pub fn start(&mut self) {
600        self.0.transfer_start();
601    }
602}
603
604impl Deref for QspiIoTransferGuard<'_> {
605    type Target = QspiIoMode;
606
607    fn deref(&self) -> &Self::Target {
608        self.0
609    }
610}
611
612impl DerefMut for QspiIoTransferGuard<'_> {
613    fn deref_mut(&mut self) -> &mut Self::Target {
614        self.0
615    }
616}
617
618impl<'a> Drop for QspiIoTransferGuard<'a> {
619    fn drop(&mut self) {
620        self.0.transfer_done();
621    }
622}
623
624pub struct QspiLinearAddressing {
625    ll: QspiLowLevel,
626}
627
628impl QspiLinearAddressing {
629    /// Memory-mapped QSPI base address.
630    pub const BASE_ADDRESS: usize = QSPI_START_ADDRESS;
631
632    pub fn into_io_mode(mut self, dual_flash: bool) -> QspiIoMode {
633        self.ll.enable_io_mode(dual_flash);
634        QspiIoMode { ll: self.ll }
635    }
636
637    pub fn read_guard(&mut self) -> QspiLinearReadGuard<'_> {
638        QspiLinearReadGuard::new(self)
639    }
640}
641
642pub struct QspiLinearReadGuard<'a>(&'a mut QspiLinearAddressing);
643
644impl QspiLinearReadGuard<'_> {
645    /// Memory-mapped QSPI base address.
646    pub const BASE_ADDRESS: usize = QSPI_START_ADDRESS;
647
648    pub fn new(qspi: &mut QspiLinearAddressing) -> QspiLinearReadGuard<'_> {
649        qspi.ll
650            .0
651            .write_spi_enable(SpiEnable::builder().with_enable(true).build());
652        QspiLinearReadGuard(qspi)
653    }
654}
655
656impl Drop for QspiLinearReadGuard<'_> {
657    fn drop(&mut self) {
658        self.0
659            .ll
660            .0
661            .write_spi_enable(SpiEnable::builder().with_enable(false).build());
662    }
663}
664
665/// Reset the QSPI peripheral using the SLCR reset register for QSPI.
666///
667/// Please note that this function will interfere with an already configured
668/// QSPI instance.
669#[inline]
670pub fn reset() {
671    unsafe {
672        Slcr::with(|regs| {
673            regs.reset_ctrl().write_lqspi(
674                QspiResetControl::builder()
675                    .with_qspi_ref_reset(true)
676                    .with_cpu_1x_reset(true)
677                    .build(),
678            );
679            // Keep it in reset for some cycles.
680            for _ in 0..3 {
681                cortex_ar::asm::nop();
682            }
683            regs.reset_ctrl().write_lqspi(QspiResetControl::DEFAULT);
684        });
685    }
686}