1//! OCTOSPI Serial Peripheral Interface
2//!
3
4#![macro_use]
5
6pub mod enums;
7
8use core::marker::PhantomData;
9
10use embassy_embedded_hal::{GetConfig, SetConfig};
11use embassy_hal_internal::{into_ref, PeripheralRef};
12pub use enums::*;
13use stm32_metapac::octospi::vals::{PhaseMode, SizeInBits};
14
15use crate::dma::{word, ChannelAndRequest};
16use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed};
17use crate::mode::{Async, Blocking, Mode as PeriMode};
18use crate::pac::octospi::{vals, Octospi as Regs};
19#[cfg(octospim_v1)]
20use crate::pac::octospim::Octospim;
21use crate::rcc::{self, RccPeripheral};
22use crate::{peripherals, Peripheral};
23
24/// OPSI driver config.
25#[derive(Clone, Copy)]
26pub struct Config {
27 /// Fifo threshold used by the peripheral to generate the interrupt indicating data
28 /// or space is available in the FIFO
29 pub fifo_threshold: FIFOThresholdLevel,
30 /// Indicates the type of external device connected
31 pub memory_type: MemoryType, // Need to add an additional enum to provide this public interface
32 /// Defines the size of the external device connected to the OSPI corresponding
33 /// to the number of address bits required to access the device
34 pub device_size: MemorySize,
35 /// Sets the minimum number of clock cycles that the chip select signal must be held high
36 /// between commands
37 pub chip_select_high_time: ChipSelectHighTime,
38 /// Enables the free running clock
39 pub free_running_clock: bool,
40 /// Sets the clock level when the device is not selected
41 pub clock_mode: bool,
42 /// Indicates the wrap size corresponding to the external device configuration
43 pub wrap_size: WrapSize,
44 /// Specified the prescaler factor used for generating the external clock based
45 /// on the AHB clock
46 pub clock_prescaler: u8,
47 /// Allows the delay of 1/2 cycle the data sampling to account for external
48 /// signal delays
49 pub sample_shifting: bool,
50 /// Allows hold to 1/4 cycle the data
51 pub delay_hold_quarter_cycle: bool,
52 /// Enables the transaction boundary feature and defines the boundary to release
53 /// the chip select
54 pub chip_select_boundary: u8,
55 /// Enables the delay block bypass so the sampling is not affected by the delay block
56 pub delay_block_bypass: bool,
57 /// Enables communication regulation feature. Chip select is released when the other
58 /// OctoSpi requests access to the bus
59 pub max_transfer: u8,
60 /// Enables the refresh feature, chip select is released every refresh + 1 clock cycles
61 pub refresh: u32,
62}
63
64impl Default for Config {
65 fn default() -> Self {
66 Self {
67 fifo_threshold: FIFOThresholdLevel::_16Bytes, // 32 bytes FIFO, half capacity
68 memory_type: MemoryType::Micron,
69 device_size: MemorySize::Other(0),
70 chip_select_high_time: ChipSelectHighTime::_5Cycle,
71 free_running_clock: false,
72 clock_mode: false,
73 wrap_size: WrapSize::None,
74 clock_prescaler: 0,
75 sample_shifting: false,
76 delay_hold_quarter_cycle: false,
77 chip_select_boundary: 0, // Acceptable range 0 to 31
78 delay_block_bypass: true,
79 max_transfer: 0,
80 refresh: 0,
81 }
82 }
83}
84
85/// OSPI transfer configuration.
86pub struct TransferConfig {
87 /// Instruction width (IMODE)
88 pub iwidth: OspiWidth,
89 /// Instruction Id
90 pub instruction: Option<u32>,
91 /// Number of Instruction Bytes
92 pub isize: AddressSize,
93 /// Instruction Double Transfer rate enable
94 pub idtr: bool,
95
96 /// Address width (ADMODE)
97 pub adwidth: OspiWidth,
98 /// Device memory address
99 pub address: Option<u32>,
100 /// Number of Address Bytes
101 pub adsize: AddressSize,
102 /// Address Double Transfer rate enable
103 pub addtr: bool,
104
105 /// Alternate bytes width (ABMODE)
106 pub abwidth: OspiWidth,
107 /// Alternate Bytes
108 pub alternate_bytes: Option<u32>,
109 /// Number of Alternate Bytes
110 pub absize: AddressSize,
111 /// Alternate Bytes Double Transfer rate enable
112 pub abdtr: bool,
113
114 /// Data width (DMODE)
115 pub dwidth: OspiWidth,
116 /// Data buffer
117 pub ddtr: bool,
118
119 /// Number of dummy cycles (DCYC)
120 pub dummy: DummyCycles,
121}
122
123impl Default for TransferConfig {
124 fn default() -> Self {
125 Self {
126 iwidth: OspiWidth::NONE,
127 instruction: None,
128 isize: AddressSize::_8Bit,
129 idtr: false,
130
131 adwidth: OspiWidth::NONE,
132 address: None,
133 adsize: AddressSize::_8Bit,
134 addtr: false,
135
136 abwidth: OspiWidth::NONE,
137 alternate_bytes: None,
138 absize: AddressSize::_8Bit,
139 abdtr: false,
140
141 dwidth: OspiWidth::NONE,
142 ddtr: false,
143
144 dummy: DummyCycles::_0,
145 }
146 }
147}
148
149/// Error used for Octospi implementation
150#[derive(Debug)]
151#[cfg_attr(feature = "defmt", derive(defmt::Format))]
152pub enum OspiError {
153 /// Peripheral configuration is invalid
154 InvalidConfiguration,
155 /// Operation configuration is invalid
156 InvalidCommand,
157 /// Size zero buffer passed to instruction
158 EmptyBuffer,
159}
160
161/// OSPI driver.
162pub struct Ospi<'d, T: Instance, M: PeriMode> {
163 _peri: PeripheralRef<'d, T>,
164 sck: Option<PeripheralRef<'d, AnyPin>>,
165 d0: Option<PeripheralRef<'d, AnyPin>>,
166 d1: Option<PeripheralRef<'d, AnyPin>>,
167 d2: Option<PeripheralRef<'d, AnyPin>>,
168 d3: Option<PeripheralRef<'d, AnyPin>>,
169 d4: Option<PeripheralRef<'d, AnyPin>>,
170 d5: Option<PeripheralRef<'d, AnyPin>>,
171 d6: Option<PeripheralRef<'d, AnyPin>>,
172 d7: Option<PeripheralRef<'d, AnyPin>>,
173 nss: Option<PeripheralRef<'d, AnyPin>>,
174 dqs: Option<PeripheralRef<'d, AnyPin>>,
175 dma: Option<ChannelAndRequest<'d>>,
176 _phantom: PhantomData<M>,
177 config: Config,
178 width: OspiWidth,
179}
180
181impl<'d, T: Instance, M: PeriMode> Ospi<'d, T, M> {
182 /// Enter memory mode.
183 /// The Input `read_config` is used to configure the read operation in memory mode
184 pub fn enable_memory_mapped_mode(
185 &mut self,
186 read_config: TransferConfig,
187 write_config: TransferConfig,
188 ) -> Result<(), OspiError> {
189 // Use configure command to set read config
190 self.configure_command(&read_config, None)?;
191
192 let reg = T::REGS;
193 while reg.sr().read().busy() {}
194
195 reg.ccr().modify(|r| {
196 r.set_dqse(false);
197 r.set_sioo(true);
198 });
199
200 // Set wrting configurations, there are separate registers for write configurations in memory mapped mode
201 reg.wccr().modify(|w| {
202 w.set_imode(PhaseMode::from_bits(write_config.iwidth.into()));
203 w.set_idtr(write_config.idtr);
204 w.set_isize(SizeInBits::from_bits(write_config.isize.into()));
205
206 w.set_admode(PhaseMode::from_bits(write_config.adwidth.into()));
207 w.set_addtr(write_config.idtr);
208 w.set_adsize(SizeInBits::from_bits(write_config.adsize.into()));
209
210 w.set_dmode(PhaseMode::from_bits(write_config.dwidth.into()));
211 w.set_ddtr(write_config.ddtr);
212
213 w.set_abmode(PhaseMode::from_bits(write_config.abwidth.into()));
214 w.set_dqse(true);
215 });
216
217 reg.wtcr().modify(|w| w.set_dcyc(write_config.dummy.into()));
218
219 // Enable memory mapped mode
220 reg.cr().modify(|r| {
221 r.set_fmode(crate::ospi::vals::FunctionalMode::MEMORY_MAPPED);
222 r.set_tcen(false);
223 });
224 Ok(())
225 }
226
227 /// Quit from memory mapped mode
228 pub fn disable_memory_mapped_mode(&mut self) {
229 let reg = T::REGS;
230
231 reg.cr().modify(|r| {
232 r.set_fmode(crate::ospi::vals::FunctionalMode::INDIRECT_WRITE);
233 r.set_abort(true);
234 r.set_dmaen(false);
235 r.set_en(false);
236 });
237
238 // Clear transfer complete flag
239 reg.fcr().write(|w| w.set_ctcf(true));
240
241 // Re-enable ospi
242 reg.cr().modify(|r| {
243 r.set_en(true);
244 });
245 }
246
247 fn new_inner(
248 peri: impl Peripheral<P = T> + 'd,
249 d0: Option<PeripheralRef<'d, AnyPin>>,
250 d1: Option<PeripheralRef<'d, AnyPin>>,
251 d2: Option<PeripheralRef<'d, AnyPin>>,
252 d3: Option<PeripheralRef<'d, AnyPin>>,
253 d4: Option<PeripheralRef<'d, AnyPin>>,
254 d5: Option<PeripheralRef<'d, AnyPin>>,
255 d6: Option<PeripheralRef<'d, AnyPin>>,
256 d7: Option<PeripheralRef<'d, AnyPin>>,
257 sck: Option<PeripheralRef<'d, AnyPin>>,
258 nss: Option<PeripheralRef<'d, AnyPin>>,
259 dqs: Option<PeripheralRef<'d, AnyPin>>,
260 dma: Option<ChannelAndRequest<'d>>,
261 config: Config,
262 width: OspiWidth,
263 dual_quad: bool,
264 ) -> Self {
265 into_ref!(peri);
266
267 #[cfg(octospim_v1)]
268 {
269 // RCC for octospim should be enabled before writing register
270 #[cfg(stm32l4)]
271 crate::pac::RCC.ahb2smenr().modify(|w| w.set_octospimsmen(true));
272 #[cfg(stm32u5)]
273 crate::pac::RCC.ahb2enr1().modify(|w| w.set_octospimen(true));
274 #[cfg(not(any(stm32l4, stm32u5)))]
275 crate::pac::RCC.ahb3enr().modify(|w| w.set_iomngren(true));
276
277 // Disable OctoSPI peripheral first
278 T::REGS.cr().modify(|w| {
279 w.set_en(false);
280 });
281
282 // OctoSPI IO Manager has been enabled before
283 T::OCTOSPIM_REGS.cr().modify(|w| {
284 w.set_muxen(false);
285 w.set_req2ack_time(0xff);
286 });
287
288 // Clear config
289 T::OCTOSPIM_REGS.p1cr().modify(|w| {
290 w.set_clksrc(false);
291 w.set_dqssrc(false);
292 w.set_ncssrc(false);
293 w.set_clken(false);
294 w.set_dqsen(false);
295 w.set_ncsen(false);
296 w.set_iolsrc(0);
297 w.set_iohsrc(0);
298 });
299
300 T::OCTOSPIM_REGS.p1cr().modify(|w| {
301 let octospi_src = if T::OCTOSPI_IDX == 1 { false } else { true };
302 w.set_ncsen(true);
303 w.set_ncssrc(octospi_src);
304 w.set_clken(true);
305 w.set_clksrc(octospi_src);
306 if dqs.is_some() {
307 w.set_dqsen(true);
308 w.set_dqssrc(octospi_src);
309 }
310
311 // Set OCTOSPIM IOL and IOH according to the index of OCTOSPI instance
312 if T::OCTOSPI_IDX == 1 {
313 w.set_iolen(true);
314 w.set_iolsrc(0);
315 // Enable IOH in octo and dual quad mode
316 if let OspiWidth::OCTO = width {
317 w.set_iohen(true);
318 w.set_iohsrc(0b01);
319 } else if dual_quad {
320 w.set_iohen(true);
321 w.set_iohsrc(0b00);
322 } else {
323 w.set_iohen(false);
324 w.set_iohsrc(0b00);
325 }
326 } else {
327 w.set_iolen(true);
328 w.set_iolsrc(0b10);
329 // Enable IOH in octo and dual quad mode
330 if let OspiWidth::OCTO = width {
331 w.set_iohen(true);
332 w.set_iohsrc(0b11);
333 } else if dual_quad {
334 w.set_iohen(true);
335 w.set_iohsrc(0b10);
336 } else {
337 w.set_iohen(false);
338 w.set_iohsrc(0b00);
339 }
340 }
341 });
342 }
343
344 // System configuration
345 rcc::enable_and_reset::<T>();
346 while T::REGS.sr().read().busy() {}
347
348 // Device configuration
349 T::REGS.dcr1().modify(|w| {
350 w.set_devsize(config.device_size.into());
351 w.set_mtyp(vals::MemType::from_bits(config.memory_type.into()));
352 w.set_csht(config.chip_select_high_time.into());
353 w.set_dlybyp(config.delay_block_bypass);
354 w.set_frck(false);
355 w.set_ckmode(config.clock_mode);
356 });
357
358 T::REGS.dcr2().modify(|w| {
359 w.set_wrapsize(config.wrap_size.into());
360 });
361
362 T::REGS.dcr3().modify(|w| {
363 w.set_csbound(config.chip_select_boundary);
364 #[cfg(octospi_v1)]
365 {
366 w.set_maxtran(config.max_transfer);
367 }
368 });
369
370 T::REGS.dcr4().modify(|w| {
371 w.set_refresh(config.refresh);
372 });
373
374 T::REGS.cr().modify(|w| {
375 w.set_fthres(vals::Threshold(config.fifo_threshold.into()));
376 });
377
378 // Wait for busy flag to clear
379 while T::REGS.sr().read().busy() {}
380
381 T::REGS.dcr2().modify(|w| {
382 w.set_prescaler(config.clock_prescaler);
383 });
384
385 T::REGS.cr().modify(|w| {
386 w.set_dmm(dual_quad);
387 });
388
389 T::REGS.tcr().modify(|w| {
390 w.set_sshift(match config.sample_shifting {
391 true => vals::SampleShift::HALF_CYCLE,
392 false => vals::SampleShift::NONE,
393 });
394 w.set_dhqc(config.delay_hold_quarter_cycle);
395 });
396
397 // Enable peripheral
398 T::REGS.cr().modify(|w| {
399 w.set_en(true);
400 });
401
402 // Free running clock needs to be set after peripheral enable
403 if config.free_running_clock {
404 T::REGS.dcr1().modify(|w| {
405 w.set_frck(config.free_running_clock);
406 });
407 }
408
409 Self {
410 _peri: peri,
411 sck,
412 d0,
413 d1,
414 d2,
415 d3,
416 d4,
417 d5,
418 d6,
419 d7,
420 nss,
421 dqs,
422 dma,
423 _phantom: PhantomData,
424 config,
425 width,
426 }
427 }
428
429 // Function to configure the peripheral for the requested command
430 fn configure_command(&mut self, command: &TransferConfig, data_len: Option<usize>) -> Result<(), OspiError> {
431 // Check that transaction doesn't use more than hardware initialized pins
432 if <enums::OspiWidth as Into<u8>>::into(command.iwidth) > <enums::OspiWidth as Into<u8>>::into(self.width)
433 || <enums::OspiWidth as Into<u8>>::into(command.adwidth) > <enums::OspiWidth as Into<u8>>::into(self.width)
434 || <enums::OspiWidth as Into<u8>>::into(command.abwidth) > <enums::OspiWidth as Into<u8>>::into(self.width)
435 || <enums::OspiWidth as Into<u8>>::into(command.dwidth) > <enums::OspiWidth as Into<u8>>::into(self.width)
436 {
437 return Err(OspiError::InvalidCommand);
438 }
439
440 T::REGS.cr().modify(|w| {
441 w.set_fmode(0.into());
442 });
443
444 // Configure alternate bytes
445 if let Some(ab) = command.alternate_bytes {
446 T::REGS.abr().write(|v| v.set_alternate(ab));
447 T::REGS.ccr().modify(|w| {
448 w.set_abmode(PhaseMode::from_bits(command.abwidth.into()));
449 w.set_abdtr(command.abdtr);
450 w.set_absize(SizeInBits::from_bits(command.absize.into()));
451 })
452 }
453
454 // Configure dummy cycles
455 T::REGS.tcr().modify(|w| {
456 w.set_dcyc(command.dummy.into());
457 });
458
459 // Configure data
460 if let Some(data_length) = data_len {
461 T::REGS.dlr().write(|v| {
462 v.set_dl((data_length - 1) as u32);
463 })
464 } else {
465 T::REGS.dlr().write(|v| {
466 v.set_dl((0) as u32);
467 })
468 }
469
470 // Configure instruction/address/data modes
471 T::REGS.ccr().modify(|w| {
472 w.set_imode(PhaseMode::from_bits(command.iwidth.into()));
473 w.set_idtr(command.idtr);
474 w.set_isize(SizeInBits::from_bits(command.isize.into()));
475
476 w.set_admode(PhaseMode::from_bits(command.adwidth.into()));
477 w.set_addtr(command.idtr);
478 w.set_adsize(SizeInBits::from_bits(command.adsize.into()));
479
480 w.set_dmode(PhaseMode::from_bits(command.dwidth.into()));
481 w.set_ddtr(command.ddtr);
482 });
483
484 // Set informationrequired to initiate transaction
485 if let Some(instruction) = command.instruction {
486 if let Some(address) = command.address {
487 T::REGS.ir().write(|v| {
488 v.set_instruction(instruction);
489 });
490
491 T::REGS.ar().write(|v| {
492 v.set_address(address);
493 });
494 } else {
495 // Double check requirements for delay hold and sample shifting
496 // if let None = command.data_len {
497 // if self.config.delay_hold_quarter_cycle && command.idtr {
498 // T::REGS.ccr().modify(|w| {
499 // w.set_ddtr(true);
500 // });
501 // }
502 // }
503
504 T::REGS.ir().write(|v| {
505 v.set_instruction(instruction);
506 });
507 }
508 } else {
509 if let Some(address) = command.address {
510 T::REGS.ar().write(|v| {
511 v.set_address(address);
512 });
513 } else {
514 // The only single phase transaction supported is instruction only
515 return Err(OspiError::InvalidCommand);
516 }
517 }
518
519 Ok(())
520 }
521
522 /// Function used to control or configure the target device without data transfer
523 pub fn blocking_command(&mut self, command: &TransferConfig) -> Result<(), OspiError> {
524 // Wait for peripheral to be free
525 while T::REGS.sr().read().busy() {}
526
527 // Need additional validation that command configuration doesn't have data set
528 self.configure_command(command, None)?;
529
530 // Transaction initiated by setting final configuration, i.e the instruction register
531 while !T::REGS.sr().read().tcf() {}
532 T::REGS.fcr().write(|w| {
533 w.set_ctcf(true);
534 });
535
536 Ok(())
537 }
538
539 /// Blocking read with byte by byte data transfer
540 pub fn blocking_read<W: Word>(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), OspiError> {
541 if buf.is_empty() {
542 return Err(OspiError::EmptyBuffer);
543 }
544
545 // Wait for peripheral to be free
546 while T::REGS.sr().read().busy() {}
547
548 // Ensure DMA is not enabled for this transaction
549 T::REGS.cr().modify(|w| {
550 w.set_dmaen(false);
551 });
552
553 self.configure_command(&transaction, Some(buf.len()))?;
554
555 let current_address = T::REGS.ar().read().address();
556 let current_instruction = T::REGS.ir().read().instruction();
557
558 // For a indirect read transaction, the transaction begins when the instruction/address is set
559 T::REGS
560 .cr()
561 .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_READ));
562 if T::REGS.ccr().read().admode() == vals::PhaseMode::NONE {
563 T::REGS.ir().write(|v| v.set_instruction(current_instruction));
564 } else {
565 T::REGS.ar().write(|v| v.set_address(current_address));
566 }
567
568 for idx in 0..buf.len() {
569 while !T::REGS.sr().read().tcf() && !T::REGS.sr().read().ftf() {}
570 buf[idx] = unsafe { (T::REGS.dr().as_ptr() as *mut W).read_volatile() };
571 }
572
573 while !T::REGS.sr().read().tcf() {}
574 T::REGS.fcr().write(|v| v.set_ctcf(true));
575
576 Ok(())
577 }
578
579 /// Blocking write with byte by byte data transfer
580 pub fn blocking_write<W: Word>(&mut self, buf: &[W], transaction: TransferConfig) -> Result<(), OspiError> {
581 if buf.is_empty() {
582 return Err(OspiError::EmptyBuffer);
583 }
584
585 // Wait for peripheral to be free
586 while T::REGS.sr().read().busy() {}
587
588 T::REGS.cr().modify(|w| {
589 w.set_dmaen(false);
590 });
591
592 self.configure_command(&transaction, Some(buf.len()))?;
593
594 T::REGS
595 .cr()
596 .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_WRITE));
597
598 for idx in 0..buf.len() {
599 while !T::REGS.sr().read().ftf() {}
600 unsafe { (T::REGS.dr().as_ptr() as *mut W).write_volatile(buf[idx]) };
601 }
602
603 while !T::REGS.sr().read().tcf() {}
604 T::REGS.fcr().write(|v| v.set_ctcf(true));
605
606 Ok(())
607 }
608
609 /// Set new bus configuration
610 pub fn set_config(&mut self, config: &Config) {
611 // Wait for busy flag to clear
612 while T::REGS.sr().read().busy() {}
613
614 // Disable DMA channel while configuring the peripheral
615 T::REGS.cr().modify(|w| {
616 w.set_dmaen(false);
617 });
618
619 // Device configuration
620 T::REGS.dcr1().modify(|w| {
621 w.set_devsize(config.device_size.into());
622 w.set_mtyp(vals::MemType::from_bits(config.memory_type.into()));
623 w.set_csht(config.chip_select_high_time.into());
624 w.set_dlybyp(config.delay_block_bypass);
625 w.set_frck(false);
626 w.set_ckmode(config.clock_mode);
627 });
628
629 T::REGS.dcr2().modify(|w| {
630 w.set_wrapsize(config.wrap_size.into());
631 });
632
633 T::REGS.dcr3().modify(|w| {
634 w.set_csbound(config.chip_select_boundary);
635 #[cfg(octospi_v1)]
636 {
637 w.set_maxtran(config.max_transfer);
638 }
639 });
640
641 T::REGS.dcr4().modify(|w| {
642 w.set_refresh(config.refresh);
643 });
644
645 T::REGS.cr().modify(|w| {
646 w.set_fthres(vals::Threshold(config.fifo_threshold.into()));
647 });
648
649 // Wait for busy flag to clear
650 while T::REGS.sr().read().busy() {}
651
652 T::REGS.dcr2().modify(|w| {
653 w.set_prescaler(config.clock_prescaler);
654 });
655
656 T::REGS.tcr().modify(|w| {
657 w.set_sshift(match config.sample_shifting {
658 true => vals::SampleShift::HALF_CYCLE,
659 false => vals::SampleShift::NONE,
660 });
661 w.set_dhqc(config.delay_hold_quarter_cycle);
662 });
663
664 // Enable peripheral
665 T::REGS.cr().modify(|w| {
666 w.set_en(true);
667 });
668
669 // Free running clock needs to be set after peripheral enable
670 if config.free_running_clock {
671 T::REGS.dcr1().modify(|w| {
672 w.set_frck(config.free_running_clock);
673 });
674 }
675
676 self.config = *config;
677 }
678
679 /// Get current configuration
680 pub fn get_config(&self) -> Config {
681 self.config
682 }
683}
684
685impl<'d, T: Instance> Ospi<'d, T, Blocking> {
686 /// Create new blocking OSPI driver for a single spi external chip
687 pub fn new_blocking_singlespi(
688 peri: impl Peripheral<P = T> + 'd,
689 sck: impl Peripheral<P = impl SckPin<T>> + 'd,
690 d0: impl Peripheral<P = impl D0Pin<T>> + 'd,
691 d1: impl Peripheral<P = impl D1Pin<T>> + 'd,
692 nss: impl Peripheral<P = impl NSSPin<T>> + 'd,
693 config: Config,
694 ) -> Self {
695 Self::new_inner(
696 peri,
697 new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
698 new_pin!(d1, AfType::input(Pull::None)),
699 None,
700 None,
701 None,
702 None,
703 None,
704 None,
705 new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
706 new_pin!(
707 nss,
708 AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
709 ),
710 None,
711 None,
712 config,
713 OspiWidth::SING,
714 false,
715 )
716 }
717
718 /// Create new blocking OSPI driver for a dualspi external chip
719 pub fn new_blocking_dualspi(
720 peri: impl Peripheral<P = T> + 'd,
721 sck: impl Peripheral<P = impl SckPin<T>> + 'd,
722 d0: impl Peripheral<P = impl D0Pin<T>> + 'd,
723 d1: impl Peripheral<P = impl D1Pin<T>> + 'd,
724 nss: impl Peripheral<P = impl NSSPin<T>> + 'd,
725 config: Config,
726 ) -> Self {
727 Self::new_inner(
728 peri,
729 new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
730 new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
731 None,
732 None,
733 None,
734 None,
735 None,
736 None,
737 new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
738 new_pin!(
739 nss,
740 AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
741 ),
742 None,
743 None,
744 config,
745 OspiWidth::DUAL,
746 false,
747 )
748 }
749
750 /// Create new blocking OSPI driver for a quadspi external chip
751 pub fn new_blocking_quadspi(
752 peri: impl Peripheral<P = T> + 'd,
753 sck: impl Peripheral<P = impl SckPin<T>> + 'd,
754 d0: impl Peripheral<P = impl D0Pin<T>> + 'd,
755 d1: impl Peripheral<P = impl D1Pin<T>> + 'd,
756 d2: impl Peripheral<P = impl D2Pin<T>> + 'd,
757 d3: impl Peripheral<P = impl D3Pin<T>> + 'd,
758 nss: impl Peripheral<P = impl NSSPin<T>> + 'd,
759 config: Config,
760 ) -> Self {
761 Self::new_inner(
762 peri,
763 new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
764 new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
765 new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
766 new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
767 None,
768 None,
769 None,
770 None,
771 new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
772 new_pin!(
773 nss,
774 AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
775 ),
776 None,
777 None,
778 config,
779 OspiWidth::QUAD,
780 false,
781 )
782 }
783
784 /// Create new blocking OSPI driver for two quadspi external chips
785 pub fn new_blocking_dualquadspi(
786 peri: impl Peripheral<P = T> + 'd,
787 sck: impl Peripheral<P = impl SckPin<T>> + 'd,
788 d0: impl Peripheral<P = impl D0Pin<T>> + 'd,
789 d1: impl Peripheral<P = impl D1Pin<T>> + 'd,
790 d2: impl Peripheral<P = impl D2Pin<T>> + 'd,
791 d3: impl Peripheral<P = impl D3Pin<T>> + 'd,
792 d4: impl Peripheral<P = impl D4Pin<T>> + 'd,
793 d5: impl Peripheral<P = impl D5Pin<T>> + 'd,
794 d6: impl Peripheral<P = impl D6Pin<T>> + 'd,
795 d7: impl Peripheral<P = impl D7Pin<T>> + 'd,
796 nss: impl Peripheral<P = impl NSSPin<T>> + 'd,
797 config: Config,
798 ) -> Self {
799 Self::new_inner(
800 peri,
801 new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
802 new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
803 new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
804 new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
805 new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
806 new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
807 new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
808 new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
809 new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
810 new_pin!(
811 nss,
812 AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
813 ),
814 None,
815 None,
816 config,
817 OspiWidth::QUAD,
818 true,
819 )
820 }
821
822 /// Create new blocking OSPI driver for octospi external chips
823 pub fn new_blocking_octospi(
824 peri: impl Peripheral<P = T> + 'd,
825 sck: impl Peripheral<P = impl SckPin<T>> + 'd,
826 d0: impl Peripheral<P = impl D0Pin<T>> + 'd,
827 d1: impl Peripheral<P = impl D1Pin<T>> + 'd,
828 d2: impl Peripheral<P = impl D2Pin<T>> + 'd,
829 d3: impl Peripheral<P = impl D3Pin<T>> + 'd,
830 d4: impl Peripheral<P = impl D4Pin<T>> + 'd,
831 d5: impl Peripheral<P = impl D5Pin<T>> + 'd,
832 d6: impl Peripheral<P = impl D6Pin<T>> + 'd,
833 d7: impl Peripheral<P = impl D7Pin<T>> + 'd,
834 nss: impl Peripheral<P = impl NSSPin<T>> + 'd,
835 config: Config,
836 ) -> Self {
837 Self::new_inner(
838 peri,
839 new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
840 new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
841 new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
842 new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
843 new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
844 new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
845 new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
846 new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
847 new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
848 new_pin!(
849 nss,
850 AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
851 ),
852 None,
853 None,
854 config,
855 OspiWidth::OCTO,
856 false,
857 )
858 }
859}
860
861impl<'d, T: Instance> Ospi<'d, T, Async> {
862 /// Create new blocking OSPI driver for a single spi external chip
863 pub fn new_singlespi(
864 peri: impl Peripheral<P = T> + 'd,
865 sck: impl Peripheral<P = impl SckPin<T>> + 'd,
866 d0: impl Peripheral<P = impl D0Pin<T>> + 'd,
867 d1: impl Peripheral<P = impl D1Pin<T>> + 'd,
868 nss: impl Peripheral<P = impl NSSPin<T>> + 'd,
869 dma: impl Peripheral<P = impl OctoDma<T>> + 'd,
870 config: Config,
871 ) -> Self {
872 Self::new_inner(
873 peri,
874 new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
875 new_pin!(d1, AfType::input(Pull::None)),
876 None,
877 None,
878 None,
879 None,
880 None,
881 None,
882 new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
883 new_pin!(
884 nss,
885 AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
886 ),
887 None,
888 new_dma!(dma),
889 config,
890 OspiWidth::SING,
891 false,
892 )
893 }
894
895 /// Create new blocking OSPI driver for a dualspi external chip
896 pub fn new_dualspi(
897 peri: impl Peripheral<P = T> + 'd,
898 sck: impl Peripheral<P = impl SckPin<T>> + 'd,
899 d0: impl Peripheral<P = impl D0Pin<T>> + 'd,
900 d1: impl Peripheral<P = impl D1Pin<T>> + 'd,
901 nss: impl Peripheral<P = impl NSSPin<T>> + 'd,
902 dma: impl Peripheral<P = impl OctoDma<T>> + 'd,
903 config: Config,
904 ) -> Self {
905 Self::new_inner(
906 peri,
907 new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
908 new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
909 None,
910 None,
911 None,
912 None,
913 None,
914 None,
915 new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
916 new_pin!(
917 nss,
918 AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
919 ),
920 None,
921 new_dma!(dma),
922 config,
923 OspiWidth::DUAL,
924 false,
925 )
926 }
927
928 /// Create new blocking OSPI driver for a quadspi external chip
929 pub fn new_quadspi(
930 peri: impl Peripheral<P = T> + 'd,
931 sck: impl Peripheral<P = impl SckPin<T>> + 'd,
932 d0: impl Peripheral<P = impl D0Pin<T>> + 'd,
933 d1: impl Peripheral<P = impl D1Pin<T>> + 'd,
934 d2: impl Peripheral<P = impl D2Pin<T>> + 'd,
935 d3: impl Peripheral<P = impl D3Pin<T>> + 'd,
936 nss: impl Peripheral<P = impl NSSPin<T>> + 'd,
937 dma: impl Peripheral<P = impl OctoDma<T>> + 'd,
938 config: Config,
939 ) -> Self {
940 Self::new_inner(
941 peri,
942 new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
943 new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
944 new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
945 new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
946 None,
947 None,
948 None,
949 None,
950 new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
951 new_pin!(
952 nss,
953 AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
954 ),
955 None,
956 new_dma!(dma),
957 config,
958 OspiWidth::QUAD,
959 false,
960 )
961 }
962
963 /// Create new blocking OSPI driver for two quadspi external chips
964 pub fn new_dualquadspi(
965 peri: impl Peripheral<P = T> + 'd,
966 sck: impl Peripheral<P = impl SckPin<T>> + 'd,
967 d0: impl Peripheral<P = impl D0Pin<T>> + 'd,
968 d1: impl Peripheral<P = impl D1Pin<T>> + 'd,
969 d2: impl Peripheral<P = impl D2Pin<T>> + 'd,
970 d3: impl Peripheral<P = impl D3Pin<T>> + 'd,
971 d4: impl Peripheral<P = impl D4Pin<T>> + 'd,
972 d5: impl Peripheral<P = impl D5Pin<T>> + 'd,
973 d6: impl Peripheral<P = impl D6Pin<T>> + 'd,
974 d7: impl Peripheral<P = impl D7Pin<T>> + 'd,
975 nss: impl Peripheral<P = impl NSSPin<T>> + 'd,
976 dma: impl Peripheral<P = impl OctoDma<T>> + 'd,
977 config: Config,
978 ) -> Self {
979 Self::new_inner(
980 peri,
981 new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
982 new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
983 new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
984 new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
985 new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
986 new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
987 new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
988 new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
989 new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
990 new_pin!(
991 nss,
992 AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
993 ),
994 None,
995 new_dma!(dma),
996 config,
997 OspiWidth::QUAD,
998 true,
999 )
1000 }
1001
1002 /// Create new blocking OSPI driver for octospi external chips
1003 pub fn new_octospi(
1004 peri: impl Peripheral<P = T> + 'd,
1005 sck: impl Peripheral<P = impl SckPin<T>> + 'd,
1006 d0: impl Peripheral<P = impl D0Pin<T>> + 'd,
1007 d1: impl Peripheral<P = impl D1Pin<T>> + 'd,
1008 d2: impl Peripheral<P = impl D2Pin<T>> + 'd,
1009 d3: impl Peripheral<P = impl D3Pin<T>> + 'd,
1010 d4: impl Peripheral<P = impl D4Pin<T>> + 'd,
1011 d5: impl Peripheral<P = impl D5Pin<T>> + 'd,
1012 d6: impl Peripheral<P = impl D6Pin<T>> + 'd,
1013 d7: impl Peripheral<P = impl D7Pin<T>> + 'd,
1014 nss: impl Peripheral<P = impl NSSPin<T>> + 'd,
1015 dma: impl Peripheral<P = impl OctoDma<T>> + 'd,
1016 config: Config,
1017 ) -> Self {
1018 Self::new_inner(
1019 peri,
1020 new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
1021 new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
1022 new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
1023 new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
1024 new_pin!(d4, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
1025 new_pin!(d5, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
1026 new_pin!(d6, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
1027 new_pin!(d7, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
1028 new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
1029 new_pin!(
1030 nss,
1031 AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
1032 ),
1033 None,
1034 new_dma!(dma),
1035 config,
1036 OspiWidth::OCTO,
1037 false,
1038 )
1039 }
1040
1041 /// Blocking read with DMA transfer
1042 pub fn blocking_read_dma<W: Word>(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), OspiError> {
1043 if buf.is_empty() {
1044 return Err(OspiError::EmptyBuffer);
1045 }
1046
1047 // Wait for peripheral to be free
1048 while T::REGS.sr().read().busy() {}
1049
1050 self.configure_command(&transaction, Some(buf.len()))?;
1051
1052 let current_address = T::REGS.ar().read().address();
1053 let current_instruction = T::REGS.ir().read().instruction();
1054
1055 // For a indirect read transaction, the transaction begins when the instruction/address is set
1056 T::REGS
1057 .cr()
1058 .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_READ));
1059 if T::REGS.ccr().read().admode() == vals::PhaseMode::NONE {
1060 T::REGS.ir().write(|v| v.set_instruction(current_instruction));
1061 } else {
1062 T::REGS.ar().write(|v| v.set_address(current_address));
1063 }
1064
1065 let transfer = unsafe {
1066 self.dma
1067 .as_mut()
1068 .unwrap()
1069 .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default())
1070 };
1071
1072 T::REGS.cr().modify(|w| w.set_dmaen(true));
1073
1074 transfer.blocking_wait();
1075
1076 finish_dma(T::REGS);
1077
1078 Ok(())
1079 }
1080
1081 /// Blocking write with DMA transfer
1082 pub fn blocking_write_dma<W: Word>(&mut self, buf: &[W], transaction: TransferConfig) -> Result<(), OspiError> {
1083 if buf.is_empty() {
1084 return Err(OspiError::EmptyBuffer);
1085 }
1086
1087 // Wait for peripheral to be free
1088 while T::REGS.sr().read().busy() {}
1089
1090 self.configure_command(&transaction, Some(buf.len()))?;
1091 T::REGS
1092 .cr()
1093 .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_WRITE));
1094
1095 let transfer = unsafe {
1096 self.dma
1097 .as_mut()
1098 .unwrap()
1099 .write(buf, T::REGS.dr().as_ptr() as *mut W, Default::default())
1100 };
1101
1102 T::REGS.cr().modify(|w| w.set_dmaen(true));
1103
1104 transfer.blocking_wait();
1105
1106 finish_dma(T::REGS);
1107
1108 Ok(())
1109 }
1110
1111 /// Asynchronous read from external device
1112 pub async fn read<W: Word>(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), OspiError> {
1113 if buf.is_empty() {
1114 return Err(OspiError::EmptyBuffer);
1115 }
1116
1117 // Wait for peripheral to be free
1118 while T::REGS.sr().read().busy() {}
1119
1120 self.configure_command(&transaction, Some(buf.len()))?;
1121
1122 let current_address = T::REGS.ar().read().address();
1123 let current_instruction = T::REGS.ir().read().instruction();
1124
1125 // For a indirect read transaction, the transaction begins when the instruction/address is set
1126 T::REGS
1127 .cr()
1128 .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_READ));
1129 if T::REGS.ccr().read().admode() == vals::PhaseMode::NONE {
1130 T::REGS.ir().write(|v| v.set_instruction(current_instruction));
1131 } else {
1132 T::REGS.ar().write(|v| v.set_address(current_address));
1133 }
1134
1135 let transfer = unsafe {
1136 self.dma
1137 .as_mut()
1138 .unwrap()
1139 .read(T::REGS.dr().as_ptr() as *mut W, buf, Default::default())
1140 };
1141
1142 T::REGS.cr().modify(|w| w.set_dmaen(true));
1143
1144 transfer.await;
1145
1146 finish_dma(T::REGS);
1147
1148 Ok(())
1149 }
1150
1151 /// Asynchronous write to external device
1152 pub async fn write<W: Word>(&mut self, buf: &[W], transaction: TransferConfig) -> Result<(), OspiError> {
1153 if buf.is_empty() {
1154 return Err(OspiError::EmptyBuffer);
1155 }
1156
1157 // Wait for peripheral to be free
1158 while T::REGS.sr().read().busy() {}
1159
1160 self.configure_command(&transaction, Some(buf.len()))?;
1161 T::REGS
1162 .cr()
1163 .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECT_WRITE));
1164
1165 let transfer = unsafe {
1166 self.dma
1167 .as_mut()
1168 .unwrap()
1169 .write(buf, T::REGS.dr().as_ptr() as *mut W, Default::default())
1170 };
1171
1172 T::REGS.cr().modify(|w| w.set_dmaen(true));
1173
1174 transfer.await;
1175
1176 finish_dma(T::REGS);
1177
1178 Ok(())
1179 }
1180}
1181
1182impl<'d, T: Instance, M: PeriMode> Drop for Ospi<'d, T, M> {
1183 fn drop(&mut self) {
1184 self.sck.as_ref().map(|x: &PeripheralRef<'_, AnyPin>| x.set_as_disconnected());
1185 self.d0.as_ref().map(|x: &PeripheralRef<'_, AnyPin>| x.set_as_disconnected());
1186 self.d1.as_ref().map(|x: &PeripheralRef<'_, AnyPin>| x.set_as_disconnected());
1187 self.d2.as_ref().map(|x: &PeripheralRef<'_, AnyPin>| x.set_as_disconnected());
1188 self.d3.as_ref().map(|x: &PeripheralRef<'_, AnyPin>| x.set_as_disconnected());
1189 self.d4.as_ref().map(|x: &PeripheralRef<'_, AnyPin>| x.set_as_disconnected());
1190 self.d5.as_ref().map(|x: &PeripheralRef<'_, AnyPin>| x.set_as_disconnected());
1191 self.d6.as_ref().map(|x: &PeripheralRef<'_, AnyPin>| x.set_as_disconnected());
1192 self.d7.as_ref().map(|x: &PeripheralRef<'_, AnyPin>| x.set_as_disconnected());
1193 self.nss.as_ref().map(|x: &PeripheralRef<'_, AnyPin>| x.set_as_disconnected());
1194 self.dqs.as_ref().map(|x: &PeripheralRef<'_, AnyPin>| x.set_as_disconnected());
1195
1196 rcc::disable::<T>();
1197 }
1198}
1199
1200fn finish_dma(regs: Regs) {
1201 while !regs.sr().read().tcf() {}
1202 regs.fcr().write(|v: &mut Fcr| v.set_ctcf(val:true));
1203
1204 regs.cr().modify(|w: &mut Cr| {
1205 w.set_dmaen(val:false);
1206 });
1207}
1208
1209#[cfg(octospim_v1)]
1210/// OctoSPI I/O manager instance trait.
1211pub(crate) trait SealedOctospimInstance {
1212 const OCTOSPIM_REGS: Octospim;
1213 const OCTOSPI_IDX: u8;
1214}
1215
1216/// OctoSPI instance trait.
1217pub(crate) trait SealedInstance {
1218 const REGS: Regs;
1219}
1220
1221/// OSPI instance trait.
1222#[cfg(octospim_v1)]
1223#[allow(private_bounds)]
1224pub trait Instance: Peripheral<P = Self> + SealedInstance + RccPeripheral + SealedOctospimInstance {}
1225
1226/// OSPI instance trait.
1227#[cfg(not(octospim_v1))]
1228#[allow(private_bounds)]
1229pub trait Instance: Peripheral<P = Self> + SealedInstance + RccPeripheral {}
1230
1231pin_trait!(SckPin, Instance);
1232pin_trait!(NckPin, Instance);
1233pin_trait!(D0Pin, Instance);
1234pin_trait!(D1Pin, Instance);
1235pin_trait!(D2Pin, Instance);
1236pin_trait!(D3Pin, Instance);
1237pin_trait!(D4Pin, Instance);
1238pin_trait!(D5Pin, Instance);
1239pin_trait!(D6Pin, Instance);
1240pin_trait!(D7Pin, Instance);
1241pin_trait!(DQSPin, Instance);
1242pin_trait!(NSSPin, Instance);
1243dma_trait!(OctoDma, Instance);
1244
1245// Hard-coded the octospi index, for OCTOSPIM
1246#[cfg(octospim_v1)]
1247impl SealedOctospimInstance for peripherals::OCTOSPI1 {
1248 const OCTOSPIM_REGS: Octospim = crate::pac::OCTOSPIM;
1249 const OCTOSPI_IDX: u8 = 1;
1250}
1251
1252#[cfg(all(octospim_v1, peri_octospi2))]
1253impl SealedOctospimInstance for peripherals::OCTOSPI2 {
1254 const OCTOSPIM_REGS: Octospim = crate::pac::OCTOSPIM;
1255 const OCTOSPI_IDX: u8 = 2;
1256}
1257
1258#[cfg(octospim_v1)]
1259foreach_peripheral!(
1260 (octospi, $inst:ident) => {
1261 impl SealedInstance for peripherals::$inst {
1262 const REGS: Regs = crate::pac::$inst;
1263 }
1264
1265 impl Instance for peripherals::$inst {}
1266 };
1267);
1268
1269#[cfg(not(octospim_v1))]
1270foreach_peripheral!(
1271 (octospi, $inst:ident) => {
1272 impl SealedInstance for peripherals::$inst {
1273 const REGS: Regs = crate::pac::$inst;
1274 }
1275
1276 impl Instance for peripherals::$inst {}
1277 };
1278);
1279
1280impl<'d, T: Instance, M: PeriMode> SetConfig for Ospi<'d, T, M> {
1281 type Config = Config;
1282 type ConfigError = ();
1283 fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> {
1284 self.set_config(config);
1285 Ok(())
1286 }
1287}
1288
1289impl<'d, T: Instance, M: PeriMode> GetConfig for Ospi<'d, T, M> {
1290 type Config = Config;
1291 fn get_config(&self) -> Self::Config {
1292 self.get_config()
1293 }
1294}
1295
1296/// Word sizes usable for OSPI.
1297#[allow(private_bounds)]
1298pub trait Word: word::Word {}
1299
1300macro_rules! impl_word {
1301 ($T:ty) => {
1302 impl Word for $T {}
1303 };
1304}
1305
1306impl_word!(u8);
1307impl_word!(u16);
1308impl_word!(u32);
1309