aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2024-04-04 21:41:34 +0000
committerGitHub <[email protected]>2024-04-04 21:41:34 +0000
commit921fa9af80b4f4c8e8c827f4104abceee795b630 (patch)
treee607fe6fae1f384a3d4bddeeb8a41af5250258dd
parent6c35a1769d9bce27805ed6dcbb09d1306be6e452 (diff)
parent330a3b04882a072406b9c3b4ef35d4c91c994e01 (diff)
Merge pull request #2672 from nautd/karun/main_octospi_implementation
Octospi implementation
-rw-r--r--embassy-stm32/Cargo.toml7
-rw-r--r--embassy-stm32/build.rs15
-rw-r--r--embassy-stm32/src/lib.rs2
-rw-r--r--embassy-stm32/src/ospi/enums.rs386
-rw-r--r--embassy-stm32/src/ospi/mod.rs1050
5 files changed, 1456 insertions, 4 deletions
diff --git a/embassy-stm32/Cargo.toml b/embassy-stm32/Cargo.toml
index 7c6312f6c..89b24f0eb 100644
--- a/embassy-stm32/Cargo.toml
+++ b/embassy-stm32/Cargo.toml
@@ -70,7 +70,8 @@ rand_core = "0.6.3"
70sdio-host = "0.5.0" 70sdio-host = "0.5.0"
71critical-section = "1.1" 71critical-section = "1.1"
72#stm32-metapac = { version = "15" } 72#stm32-metapac = { version = "15" }
73stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-f84633553331c2d154ee72de779a40cbb10fd1bd" } 73stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-2b7ec569a5510c324693f0515ac8ea20b12917a9" }
74
74vcell = "0.1.3" 75vcell = "0.1.3"
75nb = "1.0.0" 76nb = "1.0.0"
76stm32-fmc = "0.3.0" 77stm32-fmc = "0.3.0"
@@ -93,9 +94,9 @@ critical-section = { version = "1.1", features = ["std"] }
93[build-dependencies] 94[build-dependencies]
94proc-macro2 = "1.0.36" 95proc-macro2 = "1.0.36"
95quote = "1.0.15" 96quote = "1.0.15"
96#stm32-metapac = { version = "15", default-features = false, features = ["metadata"]}
97stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-f84633553331c2d154ee72de779a40cbb10fd1bd", default-features = false, features = ["metadata"]}
98 97
98#stm32-metapac = { version = "15", default-features = false, features = ["metadata"]}
99stm32-metapac = { git = "https://github.com/embassy-rs/stm32-data-generated", tag = "stm32-data-2b7ec569a5510c324693f0515ac8ea20b12917a9", default-features = false, features = ["metadata"]}
99 100
100[features] 101[features]
101default = ["rt"] 102default = ["rt"]
diff --git a/embassy-stm32/build.rs b/embassy-stm32/build.rs
index 057c4cee2..38b6c480c 100644
--- a/embassy-stm32/build.rs
+++ b/embassy-stm32/build.rs
@@ -1006,7 +1006,19 @@ fn main() {
1006 (("quadspi", "BK2_IO3"), quote!(crate::qspi::BK2D3Pin)), 1006 (("quadspi", "BK2_IO3"), quote!(crate::qspi::BK2D3Pin)),
1007 (("quadspi", "BK2_NCS"), quote!(crate::qspi::BK2NSSPin)), 1007 (("quadspi", "BK2_NCS"), quote!(crate::qspi::BK2NSSPin)),
1008 (("quadspi", "CLK"), quote!(crate::qspi::SckPin)), 1008 (("quadspi", "CLK"), quote!(crate::qspi::SckPin)),
1009 ].into(); 1009 (("octospi", "IO0"), quote!(crate::ospi::D0Pin)),
1010 (("octospi", "IO1"), quote!(crate::ospi::D1Pin)),
1011 (("octospi", "IO2"), quote!(crate::ospi::D2Pin)),
1012 (("octospi", "IO3"), quote!(crate::ospi::D3Pin)),
1013 (("octospi", "IO4"), quote!(crate::ospi::D4Pin)),
1014 (("octospi", "IO5"), quote!(crate::ospi::D5Pin)),
1015 (("octospi", "IO6"), quote!(crate::ospi::D6Pin)),
1016 (("octospi", "IO7"), quote!(crate::ospi::D7Pin)),
1017 (("octospi", "DQS"), quote!(crate::ospi::DQSPin)),
1018 (("octospi", "NCS"), quote!(crate::ospi::NSSPin)),
1019 (("octospi", "CLK"), quote!(crate::ospi::SckPin)),
1020 (("octospi", "NCLK"), quote!(crate::ospi::NckPin)),
1021 ].into();
1010 1022
1011 for p in METADATA.peripherals { 1023 for p in METADATA.peripherals {
1012 if let Some(regs) = &p.registers { 1024 if let Some(regs) = &p.registers {
@@ -1129,6 +1141,7 @@ fn main() {
1129 // SDMMCv1 uses the same channel for both directions, so just implement for RX 1141 // SDMMCv1 uses the same channel for both directions, so just implement for RX
1130 (("sdmmc", "RX"), quote!(crate::sdmmc::SdmmcDma)), 1142 (("sdmmc", "RX"), quote!(crate::sdmmc::SdmmcDma)),
1131 (("quadspi", "QUADSPI"), quote!(crate::qspi::QuadDma)), 1143 (("quadspi", "QUADSPI"), quote!(crate::qspi::QuadDma)),
1144 (("octospi", "OCTOSPI1"), quote!(crate::ospi::OctoDma)),
1132 (("dac", "CH1"), quote!(crate::dac::DacDma1)), 1145 (("dac", "CH1"), quote!(crate::dac::DacDma1)),
1133 (("dac", "CH2"), quote!(crate::dac::DacDma2)), 1146 (("dac", "CH2"), quote!(crate::dac::DacDma2)),
1134 (("timer", "UP"), quote!(crate::timer::UpDma)), 1147 (("timer", "UP"), quote!(crate::timer::UpDma)),
diff --git a/embassy-stm32/src/lib.rs b/embassy-stm32/src/lib.rs
index 99d8b5036..ea17f8477 100644
--- a/embassy-stm32/src/lib.rs
+++ b/embassy-stm32/src/lib.rs
@@ -64,6 +64,8 @@ pub mod ipcc;
64pub mod low_power; 64pub mod low_power;
65#[cfg(opamp)] 65#[cfg(opamp)]
66pub mod opamp; 66pub mod opamp;
67#[cfg(octospi)]
68pub mod ospi;
67#[cfg(quadspi)] 69#[cfg(quadspi)]
68pub mod qspi; 70pub mod qspi;
69#[cfg(rng)] 71#[cfg(rng)]
diff --git a/embassy-stm32/src/ospi/enums.rs b/embassy-stm32/src/ospi/enums.rs
new file mode 100644
index 000000000..4021f7ce3
--- /dev/null
+++ b/embassy-stm32/src/ospi/enums.rs
@@ -0,0 +1,386 @@
1//! Enums used in Ospi configuration.
2
3#[allow(dead_code)]
4#[derive(Copy, Clone)]
5pub(crate) enum OspiMode {
6 IndirectWrite,
7 IndirectRead,
8 AutoPolling,
9 MemoryMapped,
10}
11
12impl Into<u8> for OspiMode {
13 fn into(self) -> u8 {
14 match self {
15 OspiMode::IndirectWrite => 0b00,
16 OspiMode::IndirectRead => 0b01,
17 OspiMode::AutoPolling => 0b10,
18 OspiMode::MemoryMapped => 0b11,
19 }
20 }
21}
22
23/// Ospi lane width
24#[allow(dead_code)]
25#[derive(Copy, Clone)]
26pub enum OspiWidth {
27 /// None
28 NONE,
29 /// Single lane
30 SING,
31 /// Dual lanes
32 DUAL,
33 /// Quad lanes
34 QUAD,
35 /// Eight lanes
36 OCTO,
37}
38
39impl Into<u8> for OspiWidth {
40 fn into(self) -> u8 {
41 match self {
42 OspiWidth::NONE => 0b00,
43 OspiWidth::SING => 0b01,
44 OspiWidth::DUAL => 0b10,
45 OspiWidth::QUAD => 0b11,
46 OspiWidth::OCTO => 0b100,
47 }
48 }
49}
50
51/// Flash bank selection
52#[allow(dead_code)]
53#[derive(Copy, Clone)]
54pub enum FlashSelection {
55 /// Bank 1
56 Flash1,
57 /// Bank 2
58 Flash2,
59}
60
61impl Into<bool> for FlashSelection {
62 fn into(self) -> bool {
63 match self {
64 FlashSelection::Flash1 => false,
65 FlashSelection::Flash2 => true,
66 }
67 }
68}
69
70/// Wrap Size
71#[allow(dead_code)]
72#[allow(missing_docs)]
73#[derive(Copy, Clone)]
74pub enum WrapSize {
75 None,
76 _16Bytes,
77 _32Bytes,
78 _64Bytes,
79 _128Bytes,
80}
81
82impl Into<u8> for WrapSize {
83 fn into(self) -> u8 {
84 match self {
85 WrapSize::None => 0x00,
86 WrapSize::_16Bytes => 0x02,
87 WrapSize::_32Bytes => 0x03,
88 WrapSize::_64Bytes => 0x04,
89 WrapSize::_128Bytes => 0x05,
90 }
91 }
92}
93
94/// Memory Type
95#[allow(missing_docs)]
96#[allow(dead_code)]
97#[derive(Copy, Clone)]
98pub enum MemoryType {
99 Micron,
100 Macronix,
101 Standard,
102 MacronixRam,
103 HyperBusMemory,
104 HyperBusRegister,
105}
106
107impl Into<u8> for MemoryType {
108 fn into(self) -> u8 {
109 match self {
110 MemoryType::Micron => 0x00,
111 MemoryType::Macronix => 0x01,
112 MemoryType::Standard => 0x02,
113 MemoryType::MacronixRam => 0x03,
114 MemoryType::HyperBusMemory => 0x04,
115 MemoryType::HyperBusRegister => 0x04,
116 }
117 }
118}
119
120/// Ospi memory size.
121#[allow(missing_docs)]
122#[derive(Copy, Clone)]
123pub enum MemorySize {
124 _1KiB,
125 _2KiB,
126 _4KiB,
127 _8KiB,
128 _16KiB,
129 _32KiB,
130 _64KiB,
131 _128KiB,
132 _256KiB,
133 _512KiB,
134 _1MiB,
135 _2MiB,
136 _4MiB,
137 _8MiB,
138 _16MiB,
139 _32MiB,
140 _64MiB,
141 _128MiB,
142 _256MiB,
143 _512MiB,
144 _1GiB,
145 _2GiB,
146 _4GiB,
147 Other(u8),
148}
149
150impl Into<u8> for MemorySize {
151 fn into(self) -> u8 {
152 match self {
153 MemorySize::_1KiB => 9,
154 MemorySize::_2KiB => 10,
155 MemorySize::_4KiB => 11,
156 MemorySize::_8KiB => 12,
157 MemorySize::_16KiB => 13,
158 MemorySize::_32KiB => 14,
159 MemorySize::_64KiB => 15,
160 MemorySize::_128KiB => 16,
161 MemorySize::_256KiB => 17,
162 MemorySize::_512KiB => 18,
163 MemorySize::_1MiB => 19,
164 MemorySize::_2MiB => 20,
165 MemorySize::_4MiB => 21,
166 MemorySize::_8MiB => 22,
167 MemorySize::_16MiB => 23,
168 MemorySize::_32MiB => 24,
169 MemorySize::_64MiB => 25,
170 MemorySize::_128MiB => 26,
171 MemorySize::_256MiB => 27,
172 MemorySize::_512MiB => 28,
173 MemorySize::_1GiB => 29,
174 MemorySize::_2GiB => 30,
175 MemorySize::_4GiB => 31,
176 MemorySize::Other(val) => val,
177 }
178 }
179}
180
181/// Ospi Address size
182#[derive(Copy, Clone)]
183pub enum AddressSize {
184 /// 8-bit address
185 _8Bit,
186 /// 16-bit address
187 _16Bit,
188 /// 24-bit address
189 _24bit,
190 /// 32-bit address
191 _32bit,
192}
193
194impl Into<u8> for AddressSize {
195 fn into(self) -> u8 {
196 match self {
197 AddressSize::_8Bit => 0b00,
198 AddressSize::_16Bit => 0b01,
199 AddressSize::_24bit => 0b10,
200 AddressSize::_32bit => 0b11,
201 }
202 }
203}
204
205/// Time the Chip Select line stays high.
206#[allow(missing_docs)]
207#[derive(Copy, Clone)]
208pub enum ChipSelectHighTime {
209 _1Cycle,
210 _2Cycle,
211 _3Cycle,
212 _4Cycle,
213 _5Cycle,
214 _6Cycle,
215 _7Cycle,
216 _8Cycle,
217}
218
219impl Into<u8> for ChipSelectHighTime {
220 fn into(self) -> u8 {
221 match self {
222 ChipSelectHighTime::_1Cycle => 0,
223 ChipSelectHighTime::_2Cycle => 1,
224 ChipSelectHighTime::_3Cycle => 2,
225 ChipSelectHighTime::_4Cycle => 3,
226 ChipSelectHighTime::_5Cycle => 4,
227 ChipSelectHighTime::_6Cycle => 5,
228 ChipSelectHighTime::_7Cycle => 6,
229 ChipSelectHighTime::_8Cycle => 7,
230 }
231 }
232}
233
234/// FIFO threshold.
235#[allow(missing_docs)]
236#[derive(Copy, Clone)]
237pub enum FIFOThresholdLevel {
238 _1Bytes,
239 _2Bytes,
240 _3Bytes,
241 _4Bytes,
242 _5Bytes,
243 _6Bytes,
244 _7Bytes,
245 _8Bytes,
246 _9Bytes,
247 _10Bytes,
248 _11Bytes,
249 _12Bytes,
250 _13Bytes,
251 _14Bytes,
252 _15Bytes,
253 _16Bytes,
254 _17Bytes,
255 _18Bytes,
256 _19Bytes,
257 _20Bytes,
258 _21Bytes,
259 _22Bytes,
260 _23Bytes,
261 _24Bytes,
262 _25Bytes,
263 _26Bytes,
264 _27Bytes,
265 _28Bytes,
266 _29Bytes,
267 _30Bytes,
268 _31Bytes,
269 _32Bytes,
270}
271
272impl Into<u8> for FIFOThresholdLevel {
273 fn into(self) -> u8 {
274 match self {
275 FIFOThresholdLevel::_1Bytes => 0,
276 FIFOThresholdLevel::_2Bytes => 1,
277 FIFOThresholdLevel::_3Bytes => 2,
278 FIFOThresholdLevel::_4Bytes => 3,
279 FIFOThresholdLevel::_5Bytes => 4,
280 FIFOThresholdLevel::_6Bytes => 5,
281 FIFOThresholdLevel::_7Bytes => 6,
282 FIFOThresholdLevel::_8Bytes => 7,
283 FIFOThresholdLevel::_9Bytes => 8,
284 FIFOThresholdLevel::_10Bytes => 9,
285 FIFOThresholdLevel::_11Bytes => 10,
286 FIFOThresholdLevel::_12Bytes => 11,
287 FIFOThresholdLevel::_13Bytes => 12,
288 FIFOThresholdLevel::_14Bytes => 13,
289 FIFOThresholdLevel::_15Bytes => 14,
290 FIFOThresholdLevel::_16Bytes => 15,
291 FIFOThresholdLevel::_17Bytes => 16,
292 FIFOThresholdLevel::_18Bytes => 17,
293 FIFOThresholdLevel::_19Bytes => 18,
294 FIFOThresholdLevel::_20Bytes => 19,
295 FIFOThresholdLevel::_21Bytes => 20,
296 FIFOThresholdLevel::_22Bytes => 21,
297 FIFOThresholdLevel::_23Bytes => 22,
298 FIFOThresholdLevel::_24Bytes => 23,
299 FIFOThresholdLevel::_25Bytes => 24,
300 FIFOThresholdLevel::_26Bytes => 25,
301 FIFOThresholdLevel::_27Bytes => 26,
302 FIFOThresholdLevel::_28Bytes => 27,
303 FIFOThresholdLevel::_29Bytes => 28,
304 FIFOThresholdLevel::_30Bytes => 29,
305 FIFOThresholdLevel::_31Bytes => 30,
306 FIFOThresholdLevel::_32Bytes => 31,
307 }
308 }
309}
310
311/// Dummy cycle count
312#[allow(missing_docs)]
313#[derive(Copy, Clone)]
314pub enum DummyCycles {
315 _0,
316 _1,
317 _2,
318 _3,
319 _4,
320 _5,
321 _6,
322 _7,
323 _8,
324 _9,
325 _10,
326 _11,
327 _12,
328 _13,
329 _14,
330 _15,
331 _16,
332 _17,
333 _18,
334 _19,
335 _20,
336 _21,
337 _22,
338 _23,
339 _24,
340 _25,
341 _26,
342 _27,
343 _28,
344 _29,
345 _30,
346 _31,
347}
348
349impl Into<u8> for DummyCycles {
350 fn into(self) -> u8 {
351 match self {
352 DummyCycles::_0 => 0,
353 DummyCycles::_1 => 1,
354 DummyCycles::_2 => 2,
355 DummyCycles::_3 => 3,
356 DummyCycles::_4 => 4,
357 DummyCycles::_5 => 5,
358 DummyCycles::_6 => 6,
359 DummyCycles::_7 => 7,
360 DummyCycles::_8 => 8,
361 DummyCycles::_9 => 9,
362 DummyCycles::_10 => 10,
363 DummyCycles::_11 => 11,
364 DummyCycles::_12 => 12,
365 DummyCycles::_13 => 13,
366 DummyCycles::_14 => 14,
367 DummyCycles::_15 => 15,
368 DummyCycles::_16 => 16,
369 DummyCycles::_17 => 17,
370 DummyCycles::_18 => 18,
371 DummyCycles::_19 => 19,
372 DummyCycles::_20 => 20,
373 DummyCycles::_21 => 21,
374 DummyCycles::_22 => 22,
375 DummyCycles::_23 => 23,
376 DummyCycles::_24 => 24,
377 DummyCycles::_25 => 25,
378 DummyCycles::_26 => 26,
379 DummyCycles::_27 => 27,
380 DummyCycles::_28 => 28,
381 DummyCycles::_29 => 29,
382 DummyCycles::_30 => 30,
383 DummyCycles::_31 => 31,
384 }
385 }
386}
diff --git a/embassy-stm32/src/ospi/mod.rs b/embassy-stm32/src/ospi/mod.rs
new file mode 100644
index 000000000..398c3298f
--- /dev/null
+++ b/embassy-stm32/src/ospi/mod.rs
@@ -0,0 +1,1050 @@
1//! OCTOSPI Serial Peripheral Interface
2//!
3
4#![macro_use]
5
6pub mod enums;
7
8use embassy_embedded_hal::{GetConfig, SetConfig};
9use embassy_hal_internal::{into_ref, PeripheralRef};
10pub use enums::*;
11use stm32_metapac::octospi::vals::{PhaseMode, SizeInBits};
12
13use crate::dma::{word, Transfer};
14use crate::gpio::{AFType, AnyPin, Pull, SealedPin as _};
15use crate::pac::octospi::{vals, Octospi as Regs};
16use crate::rcc::RccPeripheral;
17use crate::{peripherals, Peripheral};
18
19/// OPSI driver config.
20#[derive(Clone, Copy)]
21pub struct Config {
22 /// Fifo threshold used by the peripheral to generate the interrupt indicating data
23 /// or space is available in the FIFO
24 pub fifo_threshold: FIFOThresholdLevel,
25 /// Indicates the type of external device connected
26 pub memory_type: MemoryType, // Need to add an additional enum to provide this public interface
27 /// Defines the size of the external device connected to the OSPI corresponding
28 /// to the number of address bits required to access the device
29 pub device_size: MemorySize,
30 /// Sets the minimum number of clock cycles that the chip select signal must be held high
31 /// between commands
32 pub chip_select_high_time: ChipSelectHighTime,
33 /// Enables the free running clock
34 pub free_running_clock: bool,
35 /// Sets the clock level when the device is not selected
36 pub clock_mode: bool,
37 /// Indicates the wrap size corresponding to the external device configuration
38 pub wrap_size: WrapSize,
39 /// Specified the prescaler factor used for generating the external clock based
40 /// on the AHB clock
41 pub clock_prescaler: u8,
42 /// Allows the delay of 1/2 cycle the data sampling to account for external
43 /// signal delays
44 pub sample_shifting: bool,
45 /// Allows hold to 1/4 cycle the data
46 pub delay_hold_quarter_cycle: bool,
47 /// Enables the transaction boundary feature and defines the boundary to release
48 /// the chip select
49 pub chip_select_boundary: u8,
50 /// Enbales the delay block bypass so the sampling is not affected by the delay block
51 pub delay_block_bypass: bool,
52 /// Enables communication regulation feature. Chip select is released when the other
53 /// OctoSpi requests access to the bus
54 pub max_transfer: u8,
55 /// Enables the refresh feature, chip select is released every refresh + 1 clock cycles
56 pub refresh: u32,
57}
58
59impl Default for Config {
60 fn default() -> Self {
61 Self {
62 fifo_threshold: FIFOThresholdLevel::_16Bytes, // 32 bytes FIFO, half capacity
63 memory_type: MemoryType::Micron,
64 device_size: MemorySize::Other(0),
65 chip_select_high_time: ChipSelectHighTime::_5Cycle,
66 free_running_clock: false,
67 clock_mode: false,
68 wrap_size: WrapSize::None,
69 clock_prescaler: 0,
70 sample_shifting: false,
71 delay_hold_quarter_cycle: false,
72 chip_select_boundary: 0, // Acceptable range 0 to 31
73 delay_block_bypass: true,
74 max_transfer: 0,
75 refresh: 0,
76 }
77 }
78}
79
80/// OSPI transfer configuration.
81pub struct TransferConfig {
82 /// Instruction width (IMODE)
83 pub iwidth: OspiWidth,
84 /// Instruction Id
85 pub instruction: Option<u32>,
86 /// Number of Instruction Bytes
87 pub isize: AddressSize,
88 /// Instruction Double Transfer rate enable
89 pub idtr: bool,
90
91 /// Address width (ADMODE)
92 pub adwidth: OspiWidth,
93 /// Device memory address
94 pub address: Option<u32>,
95 /// Number of Address Bytes
96 pub adsize: AddressSize,
97 /// Address Double Transfer rate enable
98 pub addtr: bool,
99
100 /// Alternate bytes width (ABMODE)
101 pub abwidth: OspiWidth,
102 /// Alternate Bytes
103 pub alternate_bytes: Option<u32>,
104 /// Number of Alternate Bytes
105 pub absize: AddressSize,
106 /// Alternate Bytes Double Transfer rate enable
107 pub abdtr: bool,
108
109 /// Data width (DMODE)
110 pub dwidth: OspiWidth,
111 /// Data buffer
112 pub ddtr: bool,
113
114 /// Number of dummy cycles (DCYC)
115 pub dummy: DummyCycles,
116}
117
118impl Default for TransferConfig {
119 fn default() -> Self {
120 Self {
121 iwidth: OspiWidth::NONE,
122 instruction: None,
123 isize: AddressSize::_8Bit,
124 idtr: false,
125
126 adwidth: OspiWidth::NONE,
127 address: None,
128 adsize: AddressSize::_8Bit,
129 addtr: false,
130
131 abwidth: OspiWidth::NONE,
132 alternate_bytes: None,
133 absize: AddressSize::_8Bit,
134 abdtr: false,
135
136 dwidth: OspiWidth::NONE,
137 ddtr: false,
138
139 dummy: DummyCycles::_0,
140 }
141 }
142}
143
144/// Error used for Octospi implementation
145#[derive(Debug)]
146#[cfg_attr(feature = "defmt", derive(defmt::Format))]
147pub enum OspiError {
148 /// Peripheral configuration is invalid
149 InvalidConfiguration,
150 /// Operation configuration is invalid
151 InvalidCommand,
152 /// Size zero buffer passed to instruction
153 EmptyBuffer,
154}
155
156/// OSPI driver.
157pub struct Ospi<'d, T: Instance, Dma> {
158 _peri: PeripheralRef<'d, T>,
159 sck: Option<PeripheralRef<'d, AnyPin>>,
160 d0: Option<PeripheralRef<'d, AnyPin>>,
161 d1: Option<PeripheralRef<'d, AnyPin>>,
162 d2: Option<PeripheralRef<'d, AnyPin>>,
163 d3: Option<PeripheralRef<'d, AnyPin>>,
164 d4: Option<PeripheralRef<'d, AnyPin>>,
165 d5: Option<PeripheralRef<'d, AnyPin>>,
166 d6: Option<PeripheralRef<'d, AnyPin>>,
167 d7: Option<PeripheralRef<'d, AnyPin>>,
168 nss: Option<PeripheralRef<'d, AnyPin>>,
169 dqs: Option<PeripheralRef<'d, AnyPin>>,
170 dma: PeripheralRef<'d, Dma>,
171 config: Config,
172 width: OspiWidth,
173}
174
175impl<'d, T: Instance, Dma> Ospi<'d, T, Dma> {
176 /// Create new OSPI driver for a single spi external chip
177 pub fn new_singlespi(
178 peri: impl Peripheral<P = T> + 'd,
179 sck: impl Peripheral<P = impl SckPin<T>> + 'd,
180 d0: impl Peripheral<P = impl D0Pin<T>> + 'd,
181 d1: impl Peripheral<P = impl D1Pin<T>> + 'd,
182 nss: impl Peripheral<P = impl NSSPin<T>> + 'd,
183 dma: impl Peripheral<P = Dma> + 'd,
184 config: Config,
185 ) -> Self {
186 into_ref!(peri, sck, d0, d1, nss);
187
188 sck.set_as_af_pull(sck.af_num(), AFType::OutputPushPull, Pull::None);
189 sck.set_speed(crate::gpio::Speed::VeryHigh);
190 nss.set_as_af_pull(nss.af_num(), AFType::OutputPushPull, Pull::Up);
191 nss.set_speed(crate::gpio::Speed::VeryHigh);
192 d0.set_as_af_pull(d0.af_num(), AFType::OutputPushPull, Pull::None);
193 d0.set_speed(crate::gpio::Speed::VeryHigh);
194 d1.set_as_af_pull(d1.af_num(), AFType::Input, Pull::None);
195 d1.set_speed(crate::gpio::Speed::VeryHigh);
196
197 Self::new_inner(
198 peri,
199 Some(d0.map_into()),
200 Some(d1.map_into()),
201 None,
202 None,
203 None,
204 None,
205 None,
206 None,
207 Some(sck.map_into()),
208 Some(nss.map_into()),
209 None,
210 dma,
211 config,
212 OspiWidth::SING,
213 false,
214 )
215 }
216
217 /// Create new OSPI driver for a dualspi external chip
218 pub fn new_dualspi(
219 peri: impl Peripheral<P = T> + 'd,
220 sck: impl Peripheral<P = impl SckPin<T>> + 'd,
221 d0: impl Peripheral<P = impl D0Pin<T>> + 'd,
222 d1: impl Peripheral<P = impl D1Pin<T>> + 'd,
223 nss: impl Peripheral<P = impl NSSPin<T>> + 'd,
224 dma: impl Peripheral<P = Dma> + 'd,
225 config: Config,
226 ) -> Self {
227 into_ref!(peri, sck, d0, d1, nss);
228
229 sck.set_as_af_pull(sck.af_num(), AFType::OutputPushPull, Pull::None);
230 sck.set_speed(crate::gpio::Speed::VeryHigh);
231 nss.set_as_af_pull(nss.af_num(), AFType::OutputPushPull, Pull::Up);
232 nss.set_speed(crate::gpio::Speed::VeryHigh);
233 d0.set_as_af_pull(d0.af_num(), AFType::OutputPushPull, Pull::None);
234 d0.set_speed(crate::gpio::Speed::VeryHigh);
235 d1.set_as_af_pull(d1.af_num(), AFType::OutputPushPull, Pull::None);
236 d1.set_speed(crate::gpio::Speed::VeryHigh);
237
238 Self::new_inner(
239 peri,
240 Some(d0.map_into()),
241 Some(d1.map_into()),
242 None,
243 None,
244 None,
245 None,
246 None,
247 None,
248 Some(sck.map_into()),
249 Some(nss.map_into()),
250 None,
251 dma,
252 config,
253 OspiWidth::DUAL,
254 false,
255 )
256 }
257
258 /// Create new OSPI driver for a quadspi external chip
259 pub fn new_quadspi(
260 peri: impl Peripheral<P = T> + 'd,
261 sck: impl Peripheral<P = impl SckPin<T>> + 'd,
262 d0: impl Peripheral<P = impl D0Pin<T>> + 'd,
263 d1: impl Peripheral<P = impl D1Pin<T>> + 'd,
264 d2: impl Peripheral<P = impl D2Pin<T>> + 'd,
265 d3: impl Peripheral<P = impl D3Pin<T>> + 'd,
266 nss: impl Peripheral<P = impl NSSPin<T>> + 'd,
267 dma: impl Peripheral<P = Dma> + 'd,
268 config: Config,
269 ) -> Self {
270 into_ref!(peri, sck, d0, d1, d2, d3, nss);
271
272 sck.set_as_af_pull(sck.af_num(), AFType::OutputPushPull, Pull::None);
273 sck.set_speed(crate::gpio::Speed::VeryHigh);
274 nss.set_as_af_pull(nss.af_num(), AFType::OutputPushPull, Pull::Up);
275 nss.set_speed(crate::gpio::Speed::VeryHigh);
276 d0.set_as_af_pull(d0.af_num(), AFType::OutputPushPull, Pull::None);
277 d0.set_speed(crate::gpio::Speed::VeryHigh);
278 d1.set_as_af_pull(d1.af_num(), AFType::OutputPushPull, Pull::None);
279 d1.set_speed(crate::gpio::Speed::VeryHigh);
280 d2.set_as_af_pull(d2.af_num(), AFType::OutputPushPull, Pull::None);
281 d2.set_speed(crate::gpio::Speed::VeryHigh);
282 d3.set_as_af_pull(d3.af_num(), AFType::OutputPushPull, Pull::None);
283 d3.set_speed(crate::gpio::Speed::VeryHigh);
284
285 Self::new_inner(
286 peri,
287 Some(d0.map_into()),
288 Some(d1.map_into()),
289 Some(d2.map_into()),
290 Some(d3.map_into()),
291 None,
292 None,
293 None,
294 None,
295 Some(sck.map_into()),
296 Some(nss.map_into()),
297 None,
298 dma,
299 config,
300 OspiWidth::QUAD,
301 false,
302 )
303 }
304
305 /// Create new OSPI driver for two quadspi external chips
306 pub fn new_dualquadspi(
307 peri: impl Peripheral<P = T> + 'd,
308 sck: impl Peripheral<P = impl SckPin<T>> + 'd,
309 d0: impl Peripheral<P = impl D0Pin<T>> + 'd,
310 d1: impl Peripheral<P = impl D1Pin<T>> + 'd,
311 d2: impl Peripheral<P = impl D2Pin<T>> + 'd,
312 d3: impl Peripheral<P = impl D3Pin<T>> + 'd,
313 d4: impl Peripheral<P = impl D4Pin<T>> + 'd,
314 d5: impl Peripheral<P = impl D5Pin<T>> + 'd,
315 d6: impl Peripheral<P = impl D6Pin<T>> + 'd,
316 d7: impl Peripheral<P = impl D7Pin<T>> + 'd,
317 nss: impl Peripheral<P = impl NSSPin<T>> + 'd,
318 dma: impl Peripheral<P = Dma> + 'd,
319 config: Config,
320 ) -> Self {
321 into_ref!(peri, sck, d0, d1, d2, d3, d4, d5, d6, d7, nss);
322
323 sck.set_as_af_pull(sck.af_num(), AFType::OutputPushPull, Pull::None);
324 sck.set_speed(crate::gpio::Speed::VeryHigh);
325 nss.set_as_af_pull(nss.af_num(), AFType::OutputPushPull, Pull::Up);
326 nss.set_speed(crate::gpio::Speed::VeryHigh);
327 d0.set_as_af_pull(d0.af_num(), AFType::OutputPushPull, Pull::None);
328 d0.set_speed(crate::gpio::Speed::VeryHigh);
329 d1.set_as_af_pull(d1.af_num(), AFType::OutputPushPull, Pull::None);
330 d1.set_speed(crate::gpio::Speed::VeryHigh);
331 d2.set_as_af_pull(d2.af_num(), AFType::OutputPushPull, Pull::None);
332 d2.set_speed(crate::gpio::Speed::VeryHigh);
333 d3.set_as_af_pull(d3.af_num(), AFType::OutputPushPull, Pull::None);
334 d3.set_speed(crate::gpio::Speed::VeryHigh);
335 d4.set_as_af_pull(d4.af_num(), AFType::OutputPushPull, Pull::None);
336 d4.set_speed(crate::gpio::Speed::VeryHigh);
337 d5.set_as_af_pull(d5.af_num(), AFType::OutputPushPull, Pull::None);
338 d5.set_speed(crate::gpio::Speed::VeryHigh);
339 d6.set_as_af_pull(d6.af_num(), AFType::OutputPushPull, Pull::None);
340 d6.set_speed(crate::gpio::Speed::VeryHigh);
341 d7.set_as_af_pull(d7.af_num(), AFType::OutputPushPull, Pull::None);
342 d7.set_speed(crate::gpio::Speed::VeryHigh);
343
344 Self::new_inner(
345 peri,
346 Some(d0.map_into()),
347 Some(d1.map_into()),
348 Some(d2.map_into()),
349 Some(d3.map_into()),
350 Some(d4.map_into()),
351 Some(d5.map_into()),
352 Some(d6.map_into()),
353 Some(d7.map_into()),
354 Some(sck.map_into()),
355 Some(nss.map_into()),
356 None,
357 dma,
358 config,
359 OspiWidth::QUAD,
360 true,
361 )
362 }
363
364 /// Create new OSPI driver for octospi external chips
365 pub fn new_octospi(
366 peri: impl Peripheral<P = T> + 'd,
367 sck: impl Peripheral<P = impl SckPin<T>> + 'd,
368 d0: impl Peripheral<P = impl D0Pin<T>> + 'd,
369 d1: impl Peripheral<P = impl D1Pin<T>> + 'd,
370 d2: impl Peripheral<P = impl D2Pin<T>> + 'd,
371 d3: impl Peripheral<P = impl D3Pin<T>> + 'd,
372 d4: impl Peripheral<P = impl D4Pin<T>> + 'd,
373 d5: impl Peripheral<P = impl D5Pin<T>> + 'd,
374 d6: impl Peripheral<P = impl D6Pin<T>> + 'd,
375 d7: impl Peripheral<P = impl D7Pin<T>> + 'd,
376 nss: impl Peripheral<P = impl NSSPin<T>> + 'd,
377 dma: impl Peripheral<P = Dma> + 'd,
378 config: Config,
379 ) -> Self {
380 into_ref!(peri, sck, d0, d1, d2, d3, d4, d5, d6, d7, nss);
381
382 sck.set_as_af_pull(sck.af_num(), AFType::OutputPushPull, Pull::None);
383 sck.set_speed(crate::gpio::Speed::VeryHigh);
384 nss.set_as_af_pull(nss.af_num(), AFType::OutputPushPull, Pull::Up);
385 nss.set_speed(crate::gpio::Speed::VeryHigh);
386 d0.set_as_af_pull(d0.af_num(), AFType::OutputPushPull, Pull::None);
387 d0.set_speed(crate::gpio::Speed::VeryHigh);
388 d1.set_as_af_pull(d1.af_num(), AFType::OutputPushPull, Pull::None);
389 d1.set_speed(crate::gpio::Speed::VeryHigh);
390 d2.set_as_af_pull(d2.af_num(), AFType::OutputPushPull, Pull::None);
391 d2.set_speed(crate::gpio::Speed::VeryHigh);
392 d3.set_as_af_pull(d3.af_num(), AFType::OutputPushPull, Pull::None);
393 d3.set_speed(crate::gpio::Speed::VeryHigh);
394 d4.set_as_af_pull(d4.af_num(), AFType::OutputPushPull, Pull::None);
395 d4.set_speed(crate::gpio::Speed::VeryHigh);
396 d5.set_as_af_pull(d5.af_num(), AFType::OutputPushPull, Pull::None);
397 d5.set_speed(crate::gpio::Speed::VeryHigh);
398 d6.set_as_af_pull(d6.af_num(), AFType::OutputPushPull, Pull::None);
399 d6.set_speed(crate::gpio::Speed::VeryHigh);
400 d7.set_as_af_pull(d7.af_num(), AFType::OutputPushPull, Pull::None);
401 d7.set_speed(crate::gpio::Speed::VeryHigh);
402
403 Self::new_inner(
404 peri,
405 Some(d0.map_into()),
406 Some(d1.map_into()),
407 Some(d2.map_into()),
408 Some(d3.map_into()),
409 Some(d4.map_into()),
410 Some(d5.map_into()),
411 Some(d6.map_into()),
412 Some(d7.map_into()),
413 Some(sck.map_into()),
414 Some(nss.map_into()),
415 None,
416 dma,
417 config,
418 OspiWidth::OCTO,
419 false,
420 )
421 }
422
423 fn new_inner(
424 peri: impl Peripheral<P = T> + 'd,
425 d0: Option<PeripheralRef<'d, AnyPin>>,
426 d1: Option<PeripheralRef<'d, AnyPin>>,
427 d2: Option<PeripheralRef<'d, AnyPin>>,
428 d3: Option<PeripheralRef<'d, AnyPin>>,
429 d4: Option<PeripheralRef<'d, AnyPin>>,
430 d5: Option<PeripheralRef<'d, AnyPin>>,
431 d6: Option<PeripheralRef<'d, AnyPin>>,
432 d7: Option<PeripheralRef<'d, AnyPin>>,
433 sck: Option<PeripheralRef<'d, AnyPin>>,
434 nss: Option<PeripheralRef<'d, AnyPin>>,
435 dqs: Option<PeripheralRef<'d, AnyPin>>,
436 dma: impl Peripheral<P = Dma> + 'd,
437 config: Config,
438 width: OspiWidth,
439 dual_quad: bool,
440 ) -> Self {
441 into_ref!(peri, dma);
442
443 // System configuration
444 T::enable_and_reset();
445 while T::REGS.sr().read().busy() {}
446
447 // Device configuration
448 T::REGS.dcr1().modify(|w| {
449 w.set_devsize(config.device_size.into());
450 w.set_mtyp(vals::MemType::from_bits(config.memory_type.into()));
451 w.set_csht(config.chip_select_high_time.into());
452 w.set_dlybyp(config.delay_block_bypass);
453 w.set_frck(false);
454 w.set_ckmode(config.clock_mode);
455 });
456
457 T::REGS.dcr2().modify(|w| {
458 w.set_wrapsize(config.wrap_size.into());
459 });
460
461 T::REGS.dcr3().modify(|w| {
462 w.set_csbound(config.chip_select_boundary);
463 #[cfg(octospi_v1)]
464 {
465 w.set_maxtran(config.max_transfer);
466 }
467 });
468
469 T::REGS.dcr4().modify(|w| {
470 w.set_refresh(config.refresh);
471 });
472
473 T::REGS.cr().modify(|w| {
474 w.set_fthres(vals::Threshold(config.fifo_threshold.into()));
475 });
476
477 // Wait for busy flag to clear
478 while T::REGS.sr().read().busy() {}
479
480 T::REGS.dcr2().modify(|w| {
481 w.set_prescaler(config.clock_prescaler);
482 });
483
484 T::REGS.cr().modify(|w| {
485 w.set_dmm(dual_quad);
486 });
487
488 T::REGS.tcr().modify(|w| {
489 w.set_sshift(match config.sample_shifting {
490 true => vals::SampleShift::HALFCYCLE,
491 false => vals::SampleShift::NONE,
492 });
493 w.set_dhqc(config.delay_hold_quarter_cycle);
494 });
495
496 // Enable peripheral
497 T::REGS.cr().modify(|w| {
498 w.set_en(true);
499 });
500
501 // Free running clock needs to be set after peripheral enable
502 if config.free_running_clock {
503 T::REGS.dcr1().modify(|w| {
504 w.set_frck(config.free_running_clock);
505 });
506 }
507
508 Self {
509 _peri: peri,
510 sck,
511 d0,
512 d1,
513 d2,
514 d3,
515 d4,
516 d5,
517 d6,
518 d7,
519 nss,
520 dqs,
521 dma,
522 config,
523 width,
524 }
525 }
526
527 // Function to configure the peripheral for the requested command
528 fn configure_command(&mut self, command: &TransferConfig, data_len: Option<usize>) -> Result<(), OspiError> {
529 // Check that transaction doesn't use more than hardware initialized pins
530 if <enums::OspiWidth as Into<u8>>::into(command.iwidth) > <enums::OspiWidth as Into<u8>>::into(self.width)
531 || <enums::OspiWidth as Into<u8>>::into(command.adwidth) > <enums::OspiWidth as Into<u8>>::into(self.width)
532 || <enums::OspiWidth as Into<u8>>::into(command.abwidth) > <enums::OspiWidth as Into<u8>>::into(self.width)
533 || <enums::OspiWidth as Into<u8>>::into(command.dwidth) > <enums::OspiWidth as Into<u8>>::into(self.width)
534 {
535 return Err(OspiError::InvalidCommand);
536 }
537
538 T::REGS.cr().modify(|w| {
539 w.set_fmode(0.into());
540 });
541
542 // Configure alternate bytes
543 if let Some(ab) = command.alternate_bytes {
544 T::REGS.abr().write(|v| v.set_alternate(ab));
545 T::REGS.ccr().modify(|w| {
546 w.set_abmode(PhaseMode::from_bits(command.abwidth.into()));
547 w.set_abdtr(command.abdtr);
548 w.set_absize(SizeInBits::from_bits(command.absize.into()));
549 })
550 }
551
552 // Configure dummy cycles
553 T::REGS.tcr().modify(|w| {
554 w.set_dcyc(command.dummy.into());
555 });
556
557 // Configure data
558 if let Some(data_length) = data_len {
559 T::REGS.dlr().write(|v| {
560 v.set_dl((data_length - 1) as u32);
561 })
562 } else {
563 T::REGS.dlr().write(|v| {
564 v.set_dl((0) as u32);
565 })
566 }
567
568 // Configure instruction/address/data modes
569 T::REGS.ccr().modify(|w| {
570 w.set_imode(PhaseMode::from_bits(command.iwidth.into()));
571 w.set_idtr(command.idtr);
572 w.set_isize(SizeInBits::from_bits(command.isize.into()));
573
574 w.set_admode(PhaseMode::from_bits(command.adwidth.into()));
575 w.set_addtr(command.idtr);
576 w.set_adsize(SizeInBits::from_bits(command.adsize.into()));
577
578 w.set_dmode(PhaseMode::from_bits(command.dwidth.into()));
579 w.set_ddtr(command.ddtr);
580 });
581
582 // Set informationrequired to initiate transaction
583 if let Some(instruction) = command.instruction {
584 if let Some(address) = command.address {
585 T::REGS.ir().write(|v| {
586 v.set_instruction(instruction);
587 });
588
589 T::REGS.ar().write(|v| {
590 v.set_address(address);
591 });
592 } else {
593 // Double check requirements for delay hold and sample shifting
594 // if let None = command.data_len {
595 // if self.config.delay_hold_quarter_cycle && command.idtr {
596 // T::REGS.ccr().modify(|w| {
597 // w.set_ddtr(true);
598 // });
599 // }
600 // }
601
602 T::REGS.ir().write(|v| {
603 v.set_instruction(instruction);
604 });
605 }
606 } else {
607 if let Some(address) = command.address {
608 T::REGS.ar().write(|v| {
609 v.set_address(address);
610 });
611 } else {
612 // The only single phase transaction supported is instruction only
613 return Err(OspiError::InvalidCommand);
614 }
615 }
616
617 Ok(())
618 }
619
620 /// Function used to control or configure the target device without data transfer
621 pub async fn command(&mut self, command: &TransferConfig) -> Result<(), OspiError> {
622 // Wait for peripheral to be free
623 while T::REGS.sr().read().busy() {}
624
625 // Need additional validation that command configuration doesn't have data set
626 self.configure_command(command, None)?;
627
628 // Transaction initiated by setting final configuration, i.e the instruction register
629 while !T::REGS.sr().read().tcf() {}
630 T::REGS.fcr().write(|w| {
631 w.set_ctcf(true);
632 });
633
634 Ok(())
635 }
636
637 /// Blocking read with byte by byte data transfer
638 pub fn blocking_read<W: Word>(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), OspiError> {
639 if buf.is_empty() {
640 return Err(OspiError::EmptyBuffer);
641 }
642
643 // Wait for peripheral to be free
644 while T::REGS.sr().read().busy() {}
645
646 // Ensure DMA is not enabled for this transaction
647 T::REGS.cr().modify(|w| {
648 w.set_dmaen(false);
649 });
650
651 self.configure_command(&transaction, Some(buf.len()))?;
652
653 let current_address = T::REGS.ar().read().address();
654 let current_instruction = T::REGS.ir().read().instruction();
655
656 // For a indirect read transaction, the transaction begins when the instruction/address is set
657 T::REGS.cr().modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECTREAD));
658 if T::REGS.ccr().read().admode() == vals::PhaseMode::NONE {
659 T::REGS.ir().write(|v| v.set_instruction(current_instruction));
660 } else {
661 T::REGS.ar().write(|v| v.set_address(current_address));
662 }
663
664 for idx in 0..buf.len() {
665 while !T::REGS.sr().read().tcf() && !T::REGS.sr().read().ftf() {}
666 buf[idx] = unsafe { (T::REGS.dr().as_ptr() as *mut W).read_volatile() };
667 }
668
669 while !T::REGS.sr().read().tcf() {}
670 T::REGS.fcr().write(|v| v.set_ctcf(true));
671
672 Ok(())
673 }
674
675 /// Blocking write with byte by byte data transfer
676 pub fn blocking_write<W: Word>(&mut self, buf: &[W], transaction: TransferConfig) -> Result<(), OspiError> {
677 if buf.is_empty() {
678 return Err(OspiError::EmptyBuffer);
679 }
680
681 // Wait for peripheral to be free
682 while T::REGS.sr().read().busy() {}
683
684 T::REGS.cr().modify(|w| {
685 w.set_dmaen(false);
686 });
687
688 self.configure_command(&transaction, Some(buf.len()))?;
689
690 T::REGS
691 .cr()
692 .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECTWRITE));
693
694 for idx in 0..buf.len() {
695 while !T::REGS.sr().read().ftf() {}
696 unsafe { (T::REGS.dr().as_ptr() as *mut W).write_volatile(buf[idx]) };
697 }
698
699 while !T::REGS.sr().read().tcf() {}
700 T::REGS.fcr().write(|v| v.set_ctcf(true));
701
702 Ok(())
703 }
704
705 /// Blocking read with DMA transfer
706 pub fn blocking_read_dma<W: Word>(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), OspiError>
707 where
708 Dma: OctoDma<T>,
709 {
710 if buf.is_empty() {
711 return Err(OspiError::EmptyBuffer);
712 }
713
714 // Wait for peripheral to be free
715 while T::REGS.sr().read().busy() {}
716
717 self.configure_command(&transaction, Some(buf.len()))?;
718
719 let current_address = T::REGS.ar().read().address();
720 let current_instruction = T::REGS.ir().read().instruction();
721
722 // For a indirect read transaction, the transaction begins when the instruction/address is set
723 T::REGS.cr().modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECTREAD));
724 if T::REGS.ccr().read().admode() == vals::PhaseMode::NONE {
725 T::REGS.ir().write(|v| v.set_instruction(current_instruction));
726 } else {
727 T::REGS.ar().write(|v| v.set_address(current_address));
728 }
729
730 let request = self.dma.request();
731 let transfer = unsafe {
732 Transfer::new_read(
733 &mut self.dma,
734 request,
735 T::REGS.dr().as_ptr() as *mut W,
736 buf,
737 Default::default(),
738 )
739 };
740
741 T::REGS.cr().modify(|w| w.set_dmaen(true));
742
743 transfer.blocking_wait();
744
745 finish_dma(T::REGS);
746
747 Ok(())
748 }
749
750 /// Blocking write with DMA transfer
751 pub fn blocking_write_dma<W: Word>(&mut self, buf: &[W], transaction: TransferConfig) -> Result<(), OspiError>
752 where
753 Dma: OctoDma<T>,
754 {
755 if buf.is_empty() {
756 return Err(OspiError::EmptyBuffer);
757 }
758
759 // Wait for peripheral to be free
760 while T::REGS.sr().read().busy() {}
761
762 self.configure_command(&transaction, Some(buf.len()))?;
763 T::REGS
764 .cr()
765 .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECTWRITE));
766
767 let request = self.dma.request();
768 let transfer = unsafe {
769 Transfer::new_write(
770 &mut self.dma,
771 request,
772 buf,
773 T::REGS.dr().as_ptr() as *mut W,
774 Default::default(),
775 )
776 };
777
778 T::REGS.cr().modify(|w| w.set_dmaen(true));
779
780 transfer.blocking_wait();
781
782 finish_dma(T::REGS);
783
784 Ok(())
785 }
786
787 /// Asynchronous read from external device
788 pub async fn read<W: Word>(&mut self, buf: &mut [W], transaction: TransferConfig) -> Result<(), OspiError>
789 where
790 Dma: OctoDma<T>,
791 {
792 if buf.is_empty() {
793 return Err(OspiError::EmptyBuffer);
794 }
795
796 // Wait for peripheral to be free
797 while T::REGS.sr().read().busy() {}
798
799 self.configure_command(&transaction, Some(buf.len()))?;
800
801 let current_address = T::REGS.ar().read().address();
802 let current_instruction = T::REGS.ir().read().instruction();
803
804 // For a indirect read transaction, the transaction begins when the instruction/address is set
805 T::REGS.cr().modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECTREAD));
806 if T::REGS.ccr().read().admode() == vals::PhaseMode::NONE {
807 T::REGS.ir().write(|v| v.set_instruction(current_instruction));
808 } else {
809 T::REGS.ar().write(|v| v.set_address(current_address));
810 }
811
812 let request = self.dma.request();
813 let transfer = unsafe {
814 Transfer::new_read(
815 &mut self.dma,
816 request,
817 T::REGS.dr().as_ptr() as *mut W,
818 buf,
819 Default::default(),
820 )
821 };
822
823 T::REGS.cr().modify(|w| w.set_dmaen(true));
824
825 transfer.await;
826
827 finish_dma(T::REGS);
828
829 Ok(())
830 }
831
832 /// Asynchronous write to external device
833 pub async fn write<W: Word>(&mut self, buf: &[W], transaction: TransferConfig) -> Result<(), OspiError>
834 where
835 Dma: OctoDma<T>,
836 {
837 if buf.is_empty() {
838 return Err(OspiError::EmptyBuffer);
839 }
840
841 // Wait for peripheral to be free
842 while T::REGS.sr().read().busy() {}
843
844 self.configure_command(&transaction, Some(buf.len()))?;
845 T::REGS
846 .cr()
847 .modify(|v| v.set_fmode(vals::FunctionalMode::INDIRECTWRITE));
848
849 let request = self.dma.request();
850 let transfer = unsafe {
851 Transfer::new_write(
852 &mut self.dma,
853 request,
854 buf,
855 T::REGS.dr().as_ptr() as *mut W,
856 Default::default(),
857 )
858 };
859
860 T::REGS.cr().modify(|w| w.set_dmaen(true));
861
862 transfer.await;
863
864 finish_dma(T::REGS);
865
866 Ok(())
867 }
868
869 /// Set new bus configuration
870 pub fn set_config(&mut self, config: &Config) {
871 // Wait for busy flag to clear
872 while T::REGS.sr().read().busy() {}
873
874 // Disable DMA channel while configuring the peripheral
875 T::REGS.cr().modify(|w| {
876 w.set_dmaen(false);
877 });
878
879 // Device configuration
880 T::REGS.dcr1().modify(|w| {
881 w.set_devsize(config.device_size.into());
882 w.set_mtyp(vals::MemType::from_bits(config.memory_type.into()));
883 w.set_csht(config.chip_select_high_time.into());
884 w.set_dlybyp(config.delay_block_bypass);
885 w.set_frck(false);
886 w.set_ckmode(config.clock_mode);
887 });
888
889 T::REGS.dcr2().modify(|w| {
890 w.set_wrapsize(config.wrap_size.into());
891 });
892
893 T::REGS.dcr3().modify(|w| {
894 w.set_csbound(config.chip_select_boundary);
895 #[cfg(octospi_v1)]
896 {
897 w.set_maxtran(config.max_transfer);
898 }
899 });
900
901 T::REGS.dcr4().modify(|w| {
902 w.set_refresh(config.refresh);
903 });
904
905 T::REGS.cr().modify(|w| {
906 w.set_fthres(vals::Threshold(config.fifo_threshold.into()));
907 });
908
909 // Wait for busy flag to clear
910 while T::REGS.sr().read().busy() {}
911
912 T::REGS.dcr2().modify(|w| {
913 w.set_prescaler(config.clock_prescaler);
914 });
915
916 T::REGS.tcr().modify(|w| {
917 w.set_sshift(match config.sample_shifting {
918 true => vals::SampleShift::HALFCYCLE,
919 false => vals::SampleShift::NONE,
920 });
921 w.set_dhqc(config.delay_hold_quarter_cycle);
922 });
923
924 // Enable peripheral
925 T::REGS.cr().modify(|w| {
926 w.set_en(true);
927 });
928
929 // Free running clock needs to be set after peripheral enable
930 if config.free_running_clock {
931 T::REGS.dcr1().modify(|w| {
932 w.set_frck(config.free_running_clock);
933 });
934 }
935
936 self.config = *config;
937 }
938
939 /// Get current configuration
940 pub fn get_config(&self) -> Config {
941 self.config
942 }
943}
944
945impl<'d, T: Instance, Dma> Drop for Ospi<'d, T, Dma> {
946 fn drop(&mut self) {
947 self.sck.as_ref().map(|x| x.set_as_disconnected());
948 self.d0.as_ref().map(|x| x.set_as_disconnected());
949 self.d1.as_ref().map(|x| x.set_as_disconnected());
950 self.d2.as_ref().map(|x| x.set_as_disconnected());
951 self.d3.as_ref().map(|x| x.set_as_disconnected());
952 self.d4.as_ref().map(|x| x.set_as_disconnected());
953 self.d5.as_ref().map(|x| x.set_as_disconnected());
954 self.d6.as_ref().map(|x| x.set_as_disconnected());
955 self.d7.as_ref().map(|x| x.set_as_disconnected());
956 self.nss.as_ref().map(|x| x.set_as_disconnected());
957 self.dqs.as_ref().map(|x| x.set_as_disconnected());
958
959 T::disable();
960 }
961}
962
963fn finish_dma(regs: Regs) {
964 while !regs.sr().read().tcf() {}
965 regs.fcr().write(|v| v.set_ctcf(true));
966
967 regs.cr().modify(|w| {
968 w.set_dmaen(false);
969 });
970}
971
972trait RegsExt {
973 fn dr_ptr<W>(&self) -> *mut W;
974}
975
976impl RegsExt for Regs {
977 fn dr_ptr<W>(&self) -> *mut W {
978 let dr = self.dr();
979 dr.as_ptr() as *mut W
980 }
981}
982
983pub(crate) trait SealedInstance {
984 const REGS: Regs;
985}
986
987trait SealedWord {
988 const CONFIG: u8;
989}
990
991/// OSPI instance trait.
992#[allow(private_bounds)]
993pub trait Instance: Peripheral<P = Self> + SealedInstance + RccPeripheral {}
994
995pin_trait!(SckPin, Instance);
996pin_trait!(NckPin, Instance);
997pin_trait!(D0Pin, Instance);
998pin_trait!(D1Pin, Instance);
999pin_trait!(D2Pin, Instance);
1000pin_trait!(D3Pin, Instance);
1001pin_trait!(D4Pin, Instance);
1002pin_trait!(D5Pin, Instance);
1003pin_trait!(D6Pin, Instance);
1004pin_trait!(D7Pin, Instance);
1005pin_trait!(DQSPin, Instance);
1006pin_trait!(NSSPin, Instance);
1007dma_trait!(OctoDma, Instance);
1008
1009foreach_peripheral!(
1010 (octospi, $inst:ident) => {
1011 impl SealedInstance for peripherals::$inst {
1012 const REGS: Regs = crate::pac::$inst;
1013 }
1014
1015 impl Instance for peripherals::$inst {}
1016 };
1017);
1018
1019impl<'d, T: Instance, Dma> SetConfig for Ospi<'d, T, Dma> {
1020 type Config = Config;
1021 type ConfigError = ();
1022 fn set_config(&mut self, config: &Self::Config) -> Result<(), ()> {
1023 self.set_config(config);
1024 Ok(())
1025 }
1026}
1027
1028impl<'d, T: Instance, Dma> GetConfig for Ospi<'d, T, Dma> {
1029 type Config = Config;
1030 fn get_config(&self) -> Self::Config {
1031 self.get_config()
1032 }
1033}
1034
1035/// Word sizes usable for OSPI.
1036#[allow(private_bounds)]
1037pub trait Word: word::Word + SealedWord {}
1038
1039macro_rules! impl_word {
1040 ($T:ty, $config:expr) => {
1041 impl SealedWord for $T {
1042 const CONFIG: u8 = $config;
1043 }
1044 impl Word for $T {}
1045 };
1046}
1047
1048impl_word!(u8, 8);
1049impl_word!(u16, 16);
1050impl_word!(u32, 32);