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