1#![macro_use]
4
5pub mod enums;
6
7use core::marker::PhantomData;
8
9use embassy_hal_internal::PeripheralType;
10use enums::*;
11
12use crate::dma::ChannelAndRequest;
13use crate::gpio::{AfType, AnyPin, OutputType, Pull, Speed};
14use crate::mode::{Async, Blocking, Mode as PeriMode};
15use crate::pac::quadspi::Quadspi as Regs;
16use crate::rcc::{self, RccPeripheral};
17use crate::{peripherals, Peri};
18
19pub struct TransferConfig {
21 pub iwidth: QspiWidth,
23 pub awidth: QspiWidth,
25 pub dwidth: QspiWidth,
27 pub instruction: u8,
29 pub address: Option<u32>,
31 pub dummy: DummyCycles,
33}
34
35impl Default for TransferConfig {
36 fn default() -> Self {
37 Self {
38 iwidth: QspiWidth::NONE,
39 awidth: QspiWidth::NONE,
40 dwidth: QspiWidth::NONE,
41 instruction: 0,
42 address: None,
43 dummy: DummyCycles::_0,
44 }
45 }
46}
47
48pub struct Config {
50 pub memory_size: MemorySize,
53 pub address_size: AddressSize,
55 pub prescaler: u8,
57 pub fifo_threshold: FIFOThresholdLevel,
59 pub cs_high_time: ChipSelectHighTime,
61 pub sample_shifting: SampleShifting,
63}
64
65impl Default for Config {
66 fn default() -> Self {
67 Self {
68 memory_size: MemorySize::Other(0),
69 address_size: AddressSize::_24bit,
70 prescaler: 128,
71 fifo_threshold: FIFOThresholdLevel::_17Bytes,
72 cs_high_time: ChipSelectHighTime::_5Cycle,
73 sample_shifting: SampleShifting::None,
74 }
75 }
76}
77
78#[allow(dead_code)]
80pub struct Qspi<'d, T: Instance, M: PeriMode> {
81 _peri: Peri<'d, T>,
82 sck: Option<Peri<'d, AnyPin>>,
83 d0: Option<Peri<'d, AnyPin>>,
84 d1: Option<Peri<'d, AnyPin>>,
85 d2: Option<Peri<'d, AnyPin>>,
86 d3: Option<Peri<'d, AnyPin>>,
87 nss: Option<Peri<'d, AnyPin>>,
88 dma: Option<ChannelAndRequest<'d>>,
89 _phantom: PhantomData<M>,
90 config: Config,
91}
92
93impl<'d, T: Instance, M: PeriMode> Qspi<'d, T, M> {
94 fn new_inner(
95 peri: Peri<'d, T>,
96 d0: Option<Peri<'d, AnyPin>>,
97 d1: Option<Peri<'d, AnyPin>>,
98 d2: Option<Peri<'d, AnyPin>>,
99 d3: Option<Peri<'d, AnyPin>>,
100 sck: Option<Peri<'d, AnyPin>>,
101 nss: Option<Peri<'d, AnyPin>>,
102 dma: Option<ChannelAndRequest<'d>>,
103 config: Config,
104 fsel: FlashSelection,
105 ) -> Self {
106 rcc::enable_and_reset::<T>();
107
108 while T::REGS.sr().read().busy() {}
109
110 #[cfg(stm32h7)]
111 {
112 use stm32_metapac::quadspi::regs::Cr;
113 T::REGS.cr().write_value(Cr(0));
115 while T::REGS.sr().read().busy() {}
116 T::REGS.cr().write_value(Cr(0xFF000001));
117 T::REGS.ccr().write(|w| w.set_frcm(true));
118 T::REGS.ccr().write(|w| w.set_frcm(true));
119 T::REGS.cr().write_value(Cr(0));
120 while T::REGS.sr().read().busy() {}
121 }
122
123 T::REGS.cr().modify(|w| {
124 w.set_en(true);
125 w.set_sshift(config.sample_shifting.into());
127 w.set_fthres(config.fifo_threshold.into());
128 w.set_prescaler(config.prescaler);
129 w.set_fsel(fsel.into());
130 });
131 T::REGS.dcr().modify(|w| {
132 w.set_fsize(config.memory_size.into());
133 w.set_csht(config.cs_high_time.into());
134 w.set_ckmode(true);
135 });
136
137 Self {
138 _peri: peri,
139 sck,
140 d0,
141 d1,
142 d2,
143 d3,
144 nss,
145 dma,
146 _phantom: PhantomData,
147 config,
148 }
149 }
150
151 pub fn blocking_command(&mut self, transaction: TransferConfig) {
153 #[cfg(not(stm32h7))]
154 T::REGS.cr().modify(|v| v.set_dmaen(false));
155 self.setup_transaction(QspiMode::IndirectWrite, &transaction, None);
156
157 while !T::REGS.sr().read().tcf() {}
158 T::REGS.fcr().modify(|v| v.set_ctcf(true));
159 }
160
161 pub fn blocking_read(&mut self, buf: &mut [u8], transaction: TransferConfig) {
163 #[cfg(not(stm32h7))]
164 T::REGS.cr().modify(|v| v.set_dmaen(false));
165 self.setup_transaction(QspiMode::IndirectWrite, &transaction, Some(buf.len()));
166
167 let current_ar = T::REGS.ar().read().address();
168 T::REGS.ccr().modify(|v| {
169 v.set_fmode(QspiMode::IndirectRead.into());
170 });
171 T::REGS.ar().write(|v| {
172 v.set_address(current_ar);
173 });
174
175 for b in buf {
176 while !T::REGS.sr().read().tcf() && (T::REGS.sr().read().flevel() == 0) {}
177 *b = unsafe { (T::REGS.dr().as_ptr() as *mut u8).read_volatile() };
178 }
179
180 while !T::REGS.sr().read().tcf() {}
181 T::REGS.fcr().modify(|v| v.set_ctcf(true));
182 }
183
184 pub fn blocking_write(&mut self, buf: &[u8], transaction: TransferConfig) {
186 #[cfg(not(stm32h7))]
188 T::REGS.cr().modify(|v| v.set_dmaen(false));
189
190 self.setup_transaction(QspiMode::IndirectWrite, &transaction, Some(buf.len()));
191
192 T::REGS.ccr().modify(|v| {
193 v.set_fmode(QspiMode::IndirectWrite.into());
194 });
195
196 for &b in buf {
197 while !T::REGS.sr().read().ftf() {}
198 unsafe { (T::REGS.dr().as_ptr() as *mut u8).write_volatile(b) };
199 }
200
201 while !T::REGS.sr().read().tcf() {}
202 T::REGS.fcr().modify(|v| v.set_ctcf(true));
203 }
204
205 pub fn enable_memory_map(&mut self, transaction: &TransferConfig) {
207 T::REGS.fcr().modify(|v| {
208 v.set_csmf(true);
209 v.set_ctcf(true);
210 v.set_ctef(true);
211 v.set_ctof(true);
212 });
213 T::REGS.ccr().write(|v| {
214 v.set_fmode(QspiMode::MemoryMapped.into());
215 v.set_imode(transaction.iwidth.into());
216 v.set_instruction(transaction.instruction);
217 v.set_admode(transaction.awidth.into());
218 v.set_adsize(self.config.address_size.into());
219 v.set_dmode(transaction.dwidth.into());
220 v.set_abmode(QspiWidth::NONE.into());
221 v.set_dcyc(transaction.dummy.into());
222 });
223 }
224
225 fn setup_transaction(&mut self, fmode: QspiMode, transaction: &TransferConfig, data_len: Option<usize>) {
226 match (transaction.address, transaction.awidth) {
227 (Some(_), QspiWidth::NONE) => panic!("QSPI address can't be sent with an address width of NONE"),
228 (Some(_), _) => {}
229 (None, QspiWidth::NONE) => {}
230 (None, _) => panic!("QSPI address is not set, so the address width should be NONE"),
231 }
232
233 match (data_len, transaction.dwidth) {
234 (Some(0), _) => panic!("QSPI data must be at least one byte"),
235 (Some(_), QspiWidth::NONE) => panic!("QSPI data can't be sent with a data width of NONE"),
236 (Some(_), _) => {}
237 (None, QspiWidth::NONE) => {}
238 (None, _) => panic!("QSPI data is empty, so the data width should be NONE"),
239 }
240
241 T::REGS.fcr().modify(|v| {
242 v.set_csmf(true);
243 v.set_ctcf(true);
244 v.set_ctef(true);
245 v.set_ctof(true);
246 });
247
248 while T::REGS.sr().read().busy() {}
249
250 if let Some(len) = data_len {
251 T::REGS.dlr().write(|v| v.set_dl(len as u32 - 1));
252 }
253
254 T::REGS.ccr().write(|v| {
255 v.set_fmode(fmode.into());
256 v.set_imode(transaction.iwidth.into());
257 v.set_instruction(transaction.instruction);
258 v.set_admode(transaction.awidth.into());
259 v.set_adsize(self.config.address_size.into());
260 v.set_dmode(transaction.dwidth.into());
261 v.set_abmode(QspiWidth::NONE.into());
262 v.set_dcyc(transaction.dummy.into());
263 });
264
265 if let Some(addr) = transaction.address {
266 T::REGS.ar().write(|v| {
267 v.set_address(addr);
268 });
269 }
270 }
271}
272
273impl<'d, T: Instance> Qspi<'d, T, Blocking> {
274 pub fn new_blocking_bank1(
276 peri: Peri<'d, T>,
277 d0: Peri<'d, impl BK1D0Pin<T>>,
278 d1: Peri<'d, impl BK1D1Pin<T>>,
279 d2: Peri<'d, impl BK1D2Pin<T>>,
280 d3: Peri<'d, impl BK1D3Pin<T>>,
281 sck: Peri<'d, impl SckPin<T>>,
282 nss: Peri<'d, impl BK1NSSPin<T>>,
283 config: Config,
284 ) -> Self {
285 Self::new_inner(
286 peri,
287 new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
288 new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
289 new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
290 new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
291 new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
292 new_pin!(
293 nss,
294 AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
295 ),
296 None,
297 config,
298 FlashSelection::Flash1,
299 )
300 }
301
302 pub fn new_blocking_bank2(
304 peri: Peri<'d, T>,
305 d0: Peri<'d, impl BK2D0Pin<T>>,
306 d1: Peri<'d, impl BK2D1Pin<T>>,
307 d2: Peri<'d, impl BK2D2Pin<T>>,
308 d3: Peri<'d, impl BK2D3Pin<T>>,
309 sck: Peri<'d, impl SckPin<T>>,
310 nss: Peri<'d, impl BK2NSSPin<T>>,
311 config: Config,
312 ) -> Self {
313 Self::new_inner(
314 peri,
315 new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
316 new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
317 new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
318 new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
319 new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
320 new_pin!(
321 nss,
322 AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
323 ),
324 None,
325 config,
326 FlashSelection::Flash2,
327 )
328 }
329}
330
331impl<'d, T: Instance> Qspi<'d, T, Async> {
332 pub fn new_bank1(
334 peri: Peri<'d, T>,
335 d0: Peri<'d, impl BK1D0Pin<T>>,
336 d1: Peri<'d, impl BK1D1Pin<T>>,
337 d2: Peri<'d, impl BK1D2Pin<T>>,
338 d3: Peri<'d, impl BK1D3Pin<T>>,
339 sck: Peri<'d, impl SckPin<T>>,
340 nss: Peri<'d, impl BK1NSSPin<T>>,
341 dma: Peri<'d, impl QuadDma<T>>,
342 config: Config,
343 ) -> Self {
344 Self::new_inner(
345 peri,
346 new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
347 new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
348 new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
349 new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
350 new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
351 new_pin!(
352 nss,
353 AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
354 ),
355 new_dma!(dma),
356 config,
357 FlashSelection::Flash1,
358 )
359 }
360
361 pub fn new_bank2(
363 peri: Peri<'d, T>,
364 d0: Peri<'d, impl BK2D0Pin<T>>,
365 d1: Peri<'d, impl BK2D1Pin<T>>,
366 d2: Peri<'d, impl BK2D2Pin<T>>,
367 d3: Peri<'d, impl BK2D3Pin<T>>,
368 sck: Peri<'d, impl SckPin<T>>,
369 nss: Peri<'d, impl BK2NSSPin<T>>,
370 dma: Peri<'d, impl QuadDma<T>>,
371 config: Config,
372 ) -> Self {
373 Self::new_inner(
374 peri,
375 new_pin!(d0, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
376 new_pin!(d1, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
377 new_pin!(d2, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
378 new_pin!(d3, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
379 new_pin!(sck, AfType::output(OutputType::PushPull, Speed::VeryHigh)),
380 new_pin!(
381 nss,
382 AfType::output_pull(OutputType::PushPull, Speed::VeryHigh, Pull::Up)
383 ),
384 new_dma!(dma),
385 config,
386 FlashSelection::Flash2,
387 )
388 }
389
390 pub fn blocking_read_dma(&mut self, buf: &mut [u8], transaction: TransferConfig) {
392 let transfer = self.start_read_transfer(transaction, buf);
393 transfer.blocking_wait();
394 }
395
396 pub async fn read_dma(&mut self, buf: &mut [u8], transaction: TransferConfig) {
398 let transfer = self.start_read_transfer(transaction, buf);
399 transfer.await;
400 }
401
402 fn start_read_transfer<'a>(
403 &'a mut self,
404 transaction: TransferConfig,
405 buf: &'a mut [u8],
406 ) -> crate::dma::Transfer<'a> {
407 self.setup_transaction(QspiMode::IndirectWrite, &transaction, Some(buf.len()));
408
409 T::REGS.ccr().modify(|v| {
410 v.set_fmode(QspiMode::IndirectRead.into());
411 });
412 let current_ar = T::REGS.ar().read().address();
413 T::REGS.ar().write(|v| {
414 v.set_address(current_ar);
415 });
416
417 let transfer = unsafe {
418 self.dma
419 .as_mut()
420 .unwrap()
421 .read(T::REGS.dr().as_ptr() as *mut u8, buf, Default::default())
422 };
423
424 #[cfg(not(stm32h7))]
426 T::REGS.cr().modify(|v| v.set_dmaen(true));
427 transfer
428 }
429
430 pub fn blocking_write_dma(&mut self, buf: &[u8], transaction: TransferConfig) {
432 let transfer = self.start_write_transfer(transaction, buf);
433 transfer.blocking_wait();
434 }
435
436 pub async fn write_dma(&mut self, buf: &[u8], transaction: TransferConfig) {
438 let transfer = self.start_write_transfer(transaction, buf);
439 transfer.await;
440 }
441
442 fn start_write_transfer<'a>(&'a mut self, transaction: TransferConfig, buf: &'a [u8]) -> crate::dma::Transfer<'a> {
443 self.setup_transaction(QspiMode::IndirectWrite, &transaction, Some(buf.len()));
444
445 T::REGS.ccr().modify(|v| {
446 v.set_fmode(QspiMode::IndirectWrite.into());
447 });
448
449 let transfer = unsafe {
450 self.dma
451 .as_mut()
452 .unwrap()
453 .write(buf, T::REGS.dr().as_ptr() as *mut u8, Default::default())
454 };
455
456 #[cfg(not(stm32h7))]
458 T::REGS.cr().modify(|v| v.set_dmaen(true));
459 transfer
460 }
461}
462
463trait SealedInstance {
464 const REGS: Regs;
465}
466
467#[allow(private_bounds)]
469pub trait Instance: SealedInstance + PeripheralType + RccPeripheral {}
470
471pin_trait!(SckPin, Instance);
472pin_trait!(BK1D0Pin, Instance);
473pin_trait!(BK1D1Pin, Instance);
474pin_trait!(BK1D2Pin, Instance);
475pin_trait!(BK1D3Pin, Instance);
476pin_trait!(BK1NSSPin, Instance);
477
478pin_trait!(BK2D0Pin, Instance);
479pin_trait!(BK2D1Pin, Instance);
480pin_trait!(BK2D2Pin, Instance);
481pin_trait!(BK2D3Pin, Instance);
482pin_trait!(BK2NSSPin, Instance);
483
484dma_trait!(QuadDma, Instance);
485
486foreach_peripheral!(
487 (quadspi, $inst:ident) => {
488 impl SealedInstance for peripherals::$inst {
489 const REGS: Regs = crate::pac::$inst;
490 }
491
492 impl Instance for peripherals::$inst {}
493 };
494);