1use crate::{
2 gpio::{AlternateH, AnyPin, Pin, PA08, PA09, PA10, PA11, PB10, PB11},
3 pac::qspi::instrframe,
4 pac::{self, Mclk},
5};
6use core::marker::PhantomData;
7
8#[derive(Debug, Clone, Copy, Eq, PartialEq)]
9#[cfg_attr(feature = "defmt", derive(defmt::Format))]
10pub enum Error {
11 CommandFunctionMismatch,
13}
14
15pub struct OneShot;
17pub struct XIP;
19
20pub struct Qspi<MODE> {
21 qspi: pac::Qspi,
22 _sck: Pin<PB10, AlternateH>,
23 _cs: Pin<PB11, AlternateH>,
24 _io0: Pin<PA08, AlternateH>,
25 _io1: Pin<PA09, AlternateH>,
26 _io2: Pin<PA10, AlternateH>,
27 _io3: Pin<PA11, AlternateH>,
28 _mode: PhantomData<MODE>,
29}
30
31impl Qspi<OneShot> {
32 #[allow(clippy::too_many_arguments)]
35 pub fn new(
36 mclk: &mut Mclk,
37 qspi: pac::Qspi,
38 _sck: impl AnyPin<Id = PB10>,
39 _cs: impl AnyPin<Id = PB11>,
40 _io0: impl AnyPin<Id = PA08>,
41 _io1: impl AnyPin<Id = PA09>,
42 _io2: impl AnyPin<Id = PA10>,
43 _io3: impl AnyPin<Id = PA11>,
44 ) -> Qspi<OneShot> {
45 mclk.apbcmask().modify(|_, w| w.qspi_().set_bit());
46 mclk.ahbmask().modify(|_, w| {
48 w.qspi_().set_bit();
49 w.qspi_2x_().clear_bit()
50 });
51
52 let _sck = _sck.into().into_alternate();
53 let _cs = _cs.into().into_alternate();
54 let _io0 = _io0.into().into_alternate();
55 let _io1 = _io1.into().into_alternate();
56 let _io2 = _io2.into().into_alternate();
57 let _io3 = _io3.into().into_alternate();
58
59 qspi.ctrla().write(|w| w.swrst().set_bit());
60 qspi.baud().write(|w| unsafe {
61 w.baud().bits(29); w.cpol().clear_bit();
67 w.cpha().clear_bit()
68 });
69
70 qspi.ctrlb().write(|w| {
71 w.mode().memory();
72 w.csmode().noreload();
73 w.csmode().lastxfer();
74 w.datalen()._8bits()
75 });
76
77 qspi.ctrla().modify(|_, w| w.enable().set_bit());
78
79 Self {
80 qspi,
81 _sck,
82 _cs,
83 _io0,
84 _io1,
85 _io2,
86 _io3,
87 _mode: PhantomData,
88 }
89 }
90
91 pub fn run_command(&self, command: Command) -> Result<(), Error> {
93 match command {
94 Command::WriteEnable
96 | Command::WriteDisable
97 | Command::Reset
98 | Command::EnableReset => (),
99 _ => return Err(Error::CommandFunctionMismatch),
100 }
101
102 let tfm = TransferMode {
103 instruction_enable: true,
104 ..TransferMode::default()
105 };
106 unsafe {
107 self.run_read_instruction(command, tfm, 0, &mut [], true);
108 }
109 Ok(())
110 }
111
112 pub fn read_command(&self, command: Command, response: &mut [u8]) -> Result<(), Error> {
114 match command {
115 Command::Read
117 | Command::QuadRead
118 | Command::ReadId
119 | Command::ReadStatus
120 | Command::ReadStatus2 => (),
121 _ => return Err(Error::CommandFunctionMismatch),
122 }
123
124 let tfm = TransferMode {
125 data_enable: true,
126 instruction_enable: true,
127 ..TransferMode::default()
128 };
129 unsafe {
130 self.run_read_instruction(command, tfm, 0, response, true);
131 }
132 Ok(())
133 }
134
135 pub fn write_command(&self, command: Command, data: &[u8]) -> Result<(), Error> {
137 match command {
138 Command::PageProgram
140 | Command::QuadPageProgram
141 | Command::WriteStatus
142 | Command::WriteStatus2 => (),
143 _ => return Err(Error::CommandFunctionMismatch),
144 }
145
146 let tfm = TransferMode {
147 data_enable: !data.is_empty(),
148 instruction_enable: true,
149 ..TransferMode::default()
150 };
151 unsafe {
152 self.run_write_instruction(command, tfm, 0, data);
153 }
154 Ok(())
155 }
156
157 pub fn erase_command(&self, command: Command, address: u32) -> Result<(), Error> {
159 match command {
160 Command::EraseSector | Command::EraseBlock => {
162 let tfm = TransferMode {
163 address_enable: true,
164 instruction_enable: true,
165 ..TransferMode::default()
166 };
167 unsafe {
168 self.run_write_instruction(command, tfm, address, &[]);
169 }
170 }
171 Command::EraseChip => {
172 let tfm = TransferMode {
173 instruction_enable: true,
174 ..TransferMode::default()
175 };
176 unsafe {
177 self.run_read_instruction(command, tfm, 0, &mut [], true);
178 }
179 }
180 _ => return Err(Error::CommandFunctionMismatch),
181 }
182
183 Ok(())
184 }
185
186 pub fn read_memory(&mut self, addr: u32, buf: &mut [u8]) {
189 let tfm = TransferMode {
190 quad_width: true,
191 address_enable: true,
192 data_enable: true,
193 instruction_enable: true,
194 dummy_cycles: 8,
195 ..TransferMode::default()
196 };
197 unsafe { self.run_read_instruction(Command::QuadRead, tfm, addr, buf, true) };
198 }
199
200 pub fn write_memory(&mut self, addr: u32, buf: &[u8]) {
205 let tfm = TransferMode {
206 quad_width: true,
207 address_enable: true,
208 data_enable: true,
209 instruction_enable: true,
210 ..TransferMode::default()
211 };
212 unsafe { self.run_write_instruction(Command::QuadPageProgram, tfm, addr, buf) };
213 }
214
215 pub fn into_xip(self) -> Qspi<XIP> {
220 let tfm = TransferMode {
221 quad_width: true,
222 address_enable: true,
223 data_enable: true,
224 instruction_enable: true,
225 dummy_cycles: 8,
226 ..TransferMode::default()
227 };
228 unsafe {
229 self.run_read_instruction(Command::QuadRead, tfm, 0, &mut [], false);
230 }
231
232 Qspi::<XIP> {
233 qspi: self.qspi,
234 _sck: self._sck,
235 _cs: self._cs,
236 _io0: self._io0,
237 _io1: self._io1,
238 _io2: self._io2,
239 _io3: self._io3,
240 _mode: PhantomData,
241 }
242 }
243
244 #[allow(clippy::type_complexity)]
248 pub fn free(
249 self,
250 ) -> (
251 pac::Qspi,
252 Pin<PB10, AlternateH>,
253 Pin<PB11, AlternateH>,
254 Pin<PA08, AlternateH>,
255 Pin<PA09, AlternateH>,
256 Pin<PA10, AlternateH>,
257 Pin<PA11, AlternateH>,
258 ) {
259 (
260 self.qspi, self._sck, self._cs, self._io0, self._io1, self._io2, self._io3,
261 )
262 }
263}
264
265impl Qspi<XIP> {
267 pub fn into_oneshot(self) -> Qspi<OneShot> {
270 unsafe { self.finalize() };
271
272 Qspi::<OneShot> {
273 qspi: self.qspi,
274 _sck: self._sck,
275 _cs: self._cs,
276 _io0: self._io0,
277 _io1: self._io1,
278 _io2: self._io2,
279 _io3: self._io3,
280 _mode: PhantomData,
281 }
282 }
283}
284
285impl<MODE> Qspi<MODE> {
287 unsafe fn finalize(&self) {
288 self.qspi.ctrla().write(|w| {
289 w.enable().set_bit();
290 w.lastxfer().set_bit()
291 });
292
293 while self.qspi.intflag().read().instrend().bit_is_clear() {}
294 self.qspi.intflag().write(|w| w.instrend().set_bit());
295 while self.qspi.intflag().read().csrise().bit_is_clear() {}
296 self.qspi.intflag().write(|w| w.csrise().set_bit());
297 }
298
299 unsafe fn run_write_instruction(
300 &self,
301 command: Command,
302 tfm: TransferMode,
303 addr: u32,
304 buf: &[u8],
305 ) {
306 if command == Command::EraseSector || command == Command::EraseBlock {
307 self.qspi.instraddr().write(|w| w.addr().bits(addr));
308 }
309 self.qspi
310 .instrctrl()
311 .modify(|_, w| w.instr().bits(command.bits()));
312 self.qspi.instrframe().write(|w| {
313 tfm.instrframe(
314 w,
315 if command == Command::QuadPageProgram {
316 instrframe::Tfrtypeselect::Writememory
317 } else {
318 instrframe::Tfrtypeselect::Write
319 },
320 )
321 });
322 self.qspi.instrframe().read().bits();
323
324 if !buf.is_empty() {
325 core::ptr::copy(buf.as_ptr(), (QSPI_AHB + addr) as *mut u8, buf.len());
326 }
327
328 self.finalize();
329 }
330
331 unsafe fn run_read_instruction(
332 &self,
333 command: Command,
334 tfm: TransferMode,
335 addr: u32,
336 buf: &mut [u8],
337 finalize: bool,
338 ) {
339 self.qspi
340 .instrctrl()
341 .modify(|_, w| w.instr().bits(command.bits()));
342 self.qspi.instrframe().write(|w| {
343 tfm.instrframe(
344 w,
345 if command == Command::QuadRead {
346 instrframe::Tfrtypeselect::Readmemory
347 } else {
348 instrframe::Tfrtypeselect::Read
349 },
350 )
351 });
352 self.qspi.instrframe().read().bits();
353
354 if !buf.is_empty() {
355 core::ptr::copy((QSPI_AHB + addr) as *mut u8, buf.as_mut_ptr(), buf.len());
356 }
357
358 if finalize {
359 self.finalize();
360 }
361 }
362
363 pub fn set_clk_divider(&mut self, value: u8) {
373 self.qspi
375 .baud()
376 .write(|w| unsafe { w.baud().bits(value.saturating_sub(1)) });
377 }
378}
379
380#[derive(Default, Debug, Copy, Clone)]
381struct TransferMode {
382 quad_width: bool,
383 data_enable: bool,
384 opcode_enable: bool,
385 address_enable: bool,
386 instruction_enable: bool,
387 dummy_cycles: u8,
388}
389
390impl TransferMode {
391 unsafe fn instrframe(
392 self,
393 instrframe: &mut instrframe::W,
394 tfrtype: instrframe::Tfrtypeselect,
395 ) -> &mut instrframe::W {
396 if self.quad_width {
397 instrframe.width().quad_output();
398 } else {
399 instrframe.width().single_bit_spi();
400 }
401
402 if self.data_enable {
403 instrframe.dataen().set_bit();
404 }
405 if self.opcode_enable {
406 instrframe.dataen().set_bit();
407 }
408 if self.address_enable {
409 instrframe.addren().set_bit();
410 }
411 if self.instruction_enable {
412 instrframe.instren().set_bit();
413 }
414
415 if self.dummy_cycles > 0 {
416 instrframe.dummylen().bits(self.dummy_cycles);
417 }
418 instrframe.addrlen()._24bits();
419 instrframe.optcodeen().clear_bit();
420 instrframe.tfrtype().variant(tfrtype);
421 instrframe
422 }
423}
424
425#[repr(u8)]
426#[derive(Debug, Copy, Clone, Eq, PartialEq)]
427pub enum Command {
428 Read = 0x03,
429 QuadRead = 0x6B,
430 ReadId = 0x9F,
431 PageProgram = 0x02,
432 QuadPageProgram = 0x32,
433 ReadStatus = 0x05,
434 ReadStatus2 = 0x35,
435 WriteStatus = 0x01,
436 WriteStatus2 = 0x31,
437 EnableReset = 0x66,
438 Reset = 0x99,
439 WriteEnable = 0x06,
440 WriteDisable = 0x04,
441 EraseSector = 0x20,
442 EraseBlock = 0xD8,
443 EraseChip = 0xC7,
444}
445
446impl Command {
447 fn bits(self) -> u8 {
448 self as u8
449 }
450}
451
452const QSPI_AHB: u32 = 0x04000000;