stm32f7xx_hal/
qspi.rs

1//! QUADSPI driver for the STM32F7. Supports INDIRECT mode only, using DMA or polling I/O.
2
3use as_slice::AsSlice;
4use core::convert::TryInto;
5use core::marker::PhantomData;
6use core::ops::Deref;
7use core::pin::Pin;
8
9use crate::dma;
10use crate::pac::{QUADSPI, RCC};
11use crate::rcc::Enable;
12use crate::state;
13
14/// The QSPI driver interface.
15pub struct Qspi {
16    /// QSPI peripheral registers.
17    qspi: QUADSPI,
18    /// Address size for all transactions.
19    adsize: u8,
20}
21
22/// QSPI transaction description. Note that "advanced" settings like DDRM, DHHC,
23/// SIOO, and the use of alternate bytes are not supported at the moment.
24#[derive(Clone)]
25pub struct QspiTransaction {
26    pub iwidth: u8,
27    pub awidth: u8,
28    pub dwidth: u8,
29    pub instruction: u8,
30    pub address: Option<u32>,
31    pub dummy: u8,
32    pub data_len: Option<usize>,
33}
34
35/// QSPI errors.
36#[derive(Debug)]
37pub enum Error {
38    /// Bad input parameters.
39    BadParam,
40}
41
42/// QSPI transactions contain configurable instruction, address, and data fields.
43/// Use these constants for the `*width` fields in `QspiTransaction`.
44pub struct QspiWidth;
45
46#[allow(dead_code)]
47impl QspiWidth {
48    pub const NONE: u8 = 0b00;
49    pub const SING: u8 = 0b01;
50    pub const DUAL: u8 = 0b10;
51    pub const QUAD: u8 = 0b11;
52}
53
54/// QSPI functional mode. Only `INDIRECT_READ` and `INDIRECT_WRITE` are
55/// supported at the moment.
56struct QspiMode;
57
58#[allow(dead_code)]
59impl QspiMode {
60    pub const INDIRECT_WRITE: u8 = 0b00;
61    pub const INDIRECT_READ: u8 = 0b01;
62    pub const AUTO_POLLING: u8 = 0b10;
63    pub const MEMORY_MAPPED: u8 = 0b11;
64}
65
66impl Qspi {
67    /// Initialize and configure the QSPI flash driver.
68    /// - `size` is log2(flash size in bytes), e.g. 16 MB = 24.
69    /// - `adsize` is the number of bytes needed to specify the address (1, 2, 3, or 4).
70    pub fn new(_rcc: &mut RCC, qspi: QUADSPI, size: u8, mut adsize: u8) -> Self {
71        assert!((1..=4).contains(&adsize));
72        adsize -= 1;
73
74        // Enable QUADSPI in RCC
75        unsafe { QUADSPI::enable_unchecked() };
76
77        // Configure QSPI
78        unsafe {
79            // Single flash mode with a QSPI clock prescaler of 2 (216 / 2 = 108 MHz), FIFO
80            // threshold only matters for DMA and is set to 4 to allow word sized DMA requests
81            qspi.cr
82                .write_with_zero(|w| w.prescaler().bits(1).fthres().bits(3).en().set_bit());
83
84            // Set the device size
85            qspi.dcr.write_with_zero(|w| w.fsize().bits(size - 1));
86        }
87
88        Qspi { qspi, adsize }
89    }
90
91    /// DMA read. Wrapper around the HAL DMA driver. Performs QSPI register programming, creates a
92    /// DMA transfer from peripheral to memory, and starts the transfer. Caller can use the DMA
93    /// `wait` API to block until the transfer is complete.
94    pub fn read_all<B>(
95        &mut self,
96        data: Pin<B>,
97        transaction: QspiTransaction,
98        dma: &dma::Handle<<RxTx<QUADSPI> as dma::Target>::Instance, state::Enabled>,
99        stream: <RxTx<QUADSPI> as dma::Target>::Stream,
100    ) -> Result<dma::Transfer<RxTx<QUADSPI>, B, dma::Started>, Error>
101    where
102        B: Deref + 'static,
103        B::Target: AsSlice<Element = u8>,
104    {
105        // Only use DMA with data, for command only use `polling_read`
106        match transaction.data_len {
107            Some(data_len) => {
108                assert!(
109                    (data_len as u32) % 4 == 0,
110                    "DMA transfer must be word aligned."
111                );
112
113                // Setup the transaction registers
114                self.setup_transaction(QspiMode::INDIRECT_READ, &transaction);
115
116                // Setup DMA transfer
117                let rx_token = RxTx(PhantomData);
118                let rx_transfer = unsafe {
119                    dma::Transfer::new(
120                        dma,
121                        stream,
122                        data,
123                        rx_token,
124                        self.dr_address(),
125                        dma::Direction::PeripheralToMemory,
126                    )
127                };
128
129                let rx_transfer = rx_transfer.start(dma);
130
131                // Set DMA bit since we are using it
132                self.qspi.cr.modify(|_, w| w.dmaen().set_bit());
133
134                Ok(rx_transfer)
135            }
136            None => Err(Error::BadParam),
137        }
138    }
139
140    /// DMA write. Wrapper around the HAL DMA driver. Performs QSPI register programming, creates a
141    /// DMA transfer from memory to peripheral, and starts the transfer. Caller can use the DMA
142    /// `wait` API to block until the transfer is complete.
143    pub fn write_all<B>(
144        &mut self,
145        data: Pin<B>,
146        transaction: QspiTransaction,
147        dma: &dma::Handle<<RxTx<QUADSPI> as dma::Target>::Instance, state::Enabled>,
148        stream: <RxTx<QUADSPI> as dma::Target>::Stream,
149    ) -> Result<dma::Transfer<RxTx<QUADSPI>, B, dma::Started>, Error>
150    where
151        B: Deref + 'static,
152        B::Target: AsSlice<Element = u8>,
153    {
154        // Only use DMA with data, for command only use `polling_write`
155        match transaction.data_len {
156            Some(data_len) => {
157                assert!(
158                    (data_len as u32) % 4 == 0,
159                    "DMA transfer must be word aligned."
160                );
161
162                // Setup the transaction registers
163                self.setup_transaction(QspiMode::INDIRECT_WRITE, &transaction);
164
165                // Setup DMA transfer
166                let tx_token = RxTx(PhantomData);
167                let tx_transfer = unsafe {
168                    dma::Transfer::new(
169                        dma,
170                        stream,
171                        data,
172                        tx_token,
173                        self.dr_address(),
174                        dma::Direction::MemoryToPeripheral,
175                    )
176                };
177
178                let tx_transfer = tx_transfer.start(dma);
179
180                // Set DMA bit since we are using it
181                self.qspi.cr.modify(|_, w| w.dmaen().set_bit());
182
183                Ok(tx_transfer)
184            }
185            None => Err(Error::BadParam),
186        }
187    }
188
189    /// Polling indirect read. Can also be used to perform transactions with no data.
190    pub fn read(&mut self, buf: &mut [u8], transaction: QspiTransaction) -> Result<(), Error> {
191        // Clear DMA bit since we are not using it
192        self.qspi.cr.modify(|_, w| w.dmaen().clear_bit());
193
194        // Setup the transaction registers
195        self.setup_transaction(QspiMode::INDIRECT_READ, &transaction);
196
197        // If the transaction has data, read it word-by-word from the data register
198        if let Some(len) = transaction.data_len {
199            let mut idx: usize = 0;
200            while idx < len {
201                // Check if there are bytes in the FIFO
202                let num_bytes = self.qspi.sr.read().flevel().bits();
203                if num_bytes > 0 {
204                    // Read a word
205                    let word = self.qspi.dr.read().data().bits();
206
207                    // Unpack the word
208                    let num_unpack = if num_bytes >= 4 { 4 } else { num_bytes };
209                    for i in 0..num_unpack {
210                        buf[idx] = ((word & (0xFF << (i * 8))) >> (i * 8)).try_into().unwrap();
211                        idx += 1;
212                    }
213                }
214            }
215        }
216
217        Ok(())
218    }
219
220    /// Polling indirect write.
221    pub fn write(&mut self, buf: &[u8], transaction: QspiTransaction) -> Result<(), Error> {
222        // Clear DMA bit since we are not using it
223        self.qspi.cr.modify(|_, w| w.dmaen().clear_bit());
224
225        // Setup the transaction registers
226        self.setup_transaction(QspiMode::INDIRECT_WRITE, &transaction);
227
228        // If the transaction has data, write it word-by-word to the data register
229        if let Some(len) = transaction.data_len {
230            let mut idx: usize = 0;
231            while idx < len {
232                // Check if the FIFO is empty
233                let num_bytes = self.qspi.sr.read().flevel().bits();
234                if num_bytes == 0 {
235                    // Pack the word
236                    let mut word: u32 = 0;
237                    let num_pack = if (len - idx) >= 4 { 4 } else { len - idx };
238                    for i in 0..num_pack {
239                        word |= (buf[idx] as u32) << (i * 8);
240                        idx += 1;
241                    }
242
243                    // Write a word
244                    unsafe {
245                        self.qspi.dr.write(|w| w.data().bits(word));
246                    }
247                }
248            }
249        }
250
251        Ok(())
252    }
253
254    /// Map from QspiTransaction to QSPI registers.
255    fn setup_transaction(&mut self, fmode: u8, transaction: &QspiTransaction) {
256        unsafe {
257            // Clear any prior status flags
258            self.qspi.fcr.write(|w| w.bits(0x1B));
259
260            // Update data length, if applicable
261            if let Some(len) = transaction.data_len {
262                self.qspi.dlr.write(|w| w.bits(len as u32 - 1));
263            }
264
265            // Update CCR register with metadata
266            self.qspi.ccr.write_with_zero(|w| {
267                w.fmode()
268                    .bits(fmode)
269                    .imode()
270                    .bits(transaction.iwidth)
271                    .admode()
272                    .bits(transaction.awidth)
273                    .dmode()
274                    .bits(transaction.dwidth)
275                    .adsize()
276                    .bits(self.adsize)
277                    .abmode()
278                    .bits(QspiWidth::NONE)
279                    .dcyc()
280                    .bits(transaction.dummy)
281                    .instruction()
282                    .bits(transaction.instruction)
283            });
284
285            // Update address register, if applicable
286            if let Some(addr) = transaction.address {
287                self.qspi.ar.write(|w| w.bits(addr));
288            }
289        }
290    }
291
292    /// Get data register address.
293    fn dr_address(&self) -> u32 {
294        &self.qspi.dr as *const _ as _
295    }
296}
297
298/// Token used for DMA transfers.
299pub struct RxTx<I>(PhantomData<I>);