1 | //! OCTOSPI Serial Peripheral Interface |
2 | //! |
3 | |
4 | #![macro_use ] |
5 | |
6 | pub mod enums; |
7 | |
8 | use core::marker::PhantomData; |
9 | |
10 | use embassy_embedded_hal::{GetConfig, SetConfig}; |
11 | use embassy_hal_internal::{into_ref, PeripheralRef}; |
12 | pub use enums::*; |
13 | use stm32_metapac::octospi::vals::{PhaseMode, SizeInBits}; |
14 | |
15 | use crate::dma::{word, ChannelAndRequest}; |
16 | use crate::gpio::{AfType, AnyPin, OutputType, Pull, SealedPin as _, Speed}; |
17 | use crate::mode::{Async, Blocking, Mode as PeriMode}; |
18 | use crate::pac::octospi::{vals, Octospi as Regs}; |
19 | #[cfg (octospim_v1)] |
20 | use crate::pac::octospim::Octospim; |
21 | use crate::rcc::{self, RccPeripheral}; |
22 | use crate::{peripherals, Peripheral}; |
23 | |
24 | /// OPSI driver config. |
25 | #[derive (Clone, Copy)] |
26 | pub 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 | |
64 | impl 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. |
86 | pub 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 | |
123 | impl 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))] |
152 | pub 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. |
162 | pub 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 | |
181 | impl<'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 | |
685 | impl<'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 | |
861 | impl<'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 | |
1182 | impl<'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 | |
1200 | fn 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. |
1211 | pub(crate) trait SealedOctospimInstance { |
1212 | const OCTOSPIM_REGS: Octospim; |
1213 | const OCTOSPI_IDX: u8; |
1214 | } |
1215 | |
1216 | /// OctoSPI instance trait. |
1217 | pub(crate) trait SealedInstance { |
1218 | const REGS: Regs; |
1219 | } |
1220 | |
1221 | /// OSPI instance trait. |
1222 | #[cfg (octospim_v1)] |
1223 | #[allow (private_bounds)] |
1224 | pub trait Instance: Peripheral<P = Self> + SealedInstance + RccPeripheral + SealedOctospimInstance {} |
1225 | |
1226 | /// OSPI instance trait. |
1227 | #[cfg (not(octospim_v1))] |
1228 | #[allow (private_bounds)] |
1229 | pub trait Instance: Peripheral<P = Self> + SealedInstance + RccPeripheral {} |
1230 | |
1231 | pin_trait!(SckPin, Instance); |
1232 | pin_trait!(NckPin, Instance); |
1233 | pin_trait!(D0Pin, Instance); |
1234 | pin_trait!(D1Pin, Instance); |
1235 | pin_trait!(D2Pin, Instance); |
1236 | pin_trait!(D3Pin, Instance); |
1237 | pin_trait!(D4Pin, Instance); |
1238 | pin_trait!(D5Pin, Instance); |
1239 | pin_trait!(D6Pin, Instance); |
1240 | pin_trait!(D7Pin, Instance); |
1241 | pin_trait!(DQSPin, Instance); |
1242 | pin_trait!(NSSPin, Instance); |
1243 | dma_trait!(OctoDma, Instance); |
1244 | |
1245 | // Hard-coded the octospi index, for OCTOSPIM |
1246 | #[cfg (octospim_v1)] |
1247 | impl 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))] |
1253 | impl SealedOctospimInstance for peripherals::OCTOSPI2 { |
1254 | const OCTOSPIM_REGS: Octospim = crate::pac::OCTOSPIM; |
1255 | const OCTOSPI_IDX: u8 = 2; |
1256 | } |
1257 | |
1258 | #[cfg (octospim_v1)] |
1259 | foreach_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))] |
1270 | foreach_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 | |
1280 | impl<'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 | |
1289 | impl<'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)] |
1298 | pub trait Word: word::Word {} |
1299 | |
1300 | macro_rules! impl_word { |
1301 | ($T:ty) => { |
1302 | impl Word for $T {} |
1303 | }; |
1304 | } |
1305 | |
1306 | impl_word!(u8); |
1307 | impl_word!(u16); |
1308 | impl_word!(u32); |
1309 | |