1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
//! QUADSPI driver for the STM32F7. Supports INDIRECT mode only, using DMA or polling I/O.

use as_slice::AsSlice;
use core::convert::TryInto;
use core::marker::PhantomData;
use core::ops::Deref;
use core::pin::Pin;

use crate::dma;
use crate::pac::{QUADSPI, RCC};
use crate::state;

/// The QSPI driver interface.
pub struct Qspi {
    /// QSPI peripheral registers.
    qspi: QUADSPI,
    /// Address size for all transactions.
    adsize: u8,
}

/// QSPI transaction description. Note that "advanced" settings like DDRM, DHHC,
/// SIOO, and the use of alternate bytes are not supported at the moment.
#[derive(Clone)]
pub struct QspiTransaction {
    pub iwidth: u8,
    pub awidth: u8,
    pub dwidth: u8,
    pub instruction: u8,
    pub address: Option<u32>,
    pub dummy: u8,
    pub data_len: Option<usize>,
}

/// QSPI errors.
#[derive(Debug)]
pub enum Error {
    /// Bad input parameters.
    BadParam,
}

/// QSPI transactions contain configurable instruction, address, and data fields.
/// Use these constants for the `*width` fields in `QspiTransaction`.
pub struct QspiWidth;

#[allow(dead_code)]
impl QspiWidth {
    pub const NONE: u8 = 0b00;
    pub const SING: u8 = 0b01;
    pub const DUAL: u8 = 0b10;
    pub const QUAD: u8 = 0b11;
}

/// QSPI functional mode. Only `INDIRECT_READ` and `INDIRECT_WRITE` are
/// supported at the moment.
struct QspiMode;

#[allow(dead_code)]
impl QspiMode {
    pub const INDIRECT_WRITE: u8 = 0b00;
    pub const INDIRECT_READ: u8 = 0b01;
    pub const AUTO_POLLING: u8 = 0b10;
    pub const MEMORY_MAPPED: u8 = 0b11;
}

impl Qspi {
    /// Initialize and configure the QSPI flash driver.
    /// - `size` is log2(flash size in bytes), e.g. 16 MB = 24.
    /// - `adsize` is the number of bytes needed to specify the address (1, 2, 3, or 4).
    pub fn new(rcc: &mut RCC, qspi: QUADSPI, size: u8, mut adsize: u8) -> Self {
        assert!((1..=4).contains(&adsize));
        adsize -= 1;

        // Enable QUADSPI in RCC
        rcc.ahb3enr.modify(|_, w| w.qspien().set_bit());

        // Configure QSPI
        unsafe {
            // Single flash mode with a QSPI clock prescaler of 2 (216 / 2 = 108 MHz), FIFO
            // threshold only matters for DMA and is set to 4 to allow word sized DMA requests
            qspi.cr
                .write_with_zero(|w| w.prescaler().bits(1).fthres().bits(3).en().set_bit());

            // Set the device size
            qspi.dcr.write_with_zero(|w| w.fsize().bits(size - 1));
        }

        Qspi { qspi, adsize }
    }

    /// DMA read. Wrapper around the HAL DMA driver. Performs QSPI register programming, creates a
    /// DMA transfer from peripheral to memory, and starts the transfer. Caller can use the DMA
    /// `wait` API to block until the transfer is complete.
    pub fn read_all<B>(
        &mut self,
        data: Pin<B>,
        transaction: QspiTransaction,
        dma: &dma::Handle<<RxTx<QUADSPI> as dma::Target>::Instance, state::Enabled>,
        stream: <RxTx<QUADSPI> as dma::Target>::Stream,
    ) -> Result<dma::Transfer<RxTx<QUADSPI>, B, dma::Started>, Error>
    where
        B: Deref + 'static,
        B::Target: AsSlice<Element = u8>,
    {
        // Only use DMA with data, for command only use `polling_read`
        match transaction.data_len {
            Some(data_len) => {
                assert!(
                    (data_len as u32) % 4 == 0,
                    "DMA transfer must be word aligned."
                );

                // Setup the transaction registers
                self.setup_transaction(QspiMode::INDIRECT_READ, &transaction);

                // Setup DMA transfer
                let rx_token = RxTx(PhantomData);
                let rx_transfer = unsafe {
                    dma::Transfer::new(
                        dma,
                        stream,
                        data,
                        rx_token,
                        self.dr_address(),
                        dma::Direction::PeripheralToMemory,
                    )
                };

                let rx_transfer = rx_transfer.start(&dma);

                // Set DMA bit since we are using it
                self.qspi.cr.modify(|_, w| w.dmaen().set_bit());

                Ok(rx_transfer)
            }
            None => Err(Error::BadParam),
        }
    }

    /// DMA write. Wrapper around the HAL DMA driver. Performs QSPI register programming, creates a
    /// DMA transfer from memory to peripheral, and starts the transfer. Caller can use the DMA
    /// `wait` API to block until the transfer is complete.
    pub fn write_all<B>(
        &mut self,
        data: Pin<B>,
        transaction: QspiTransaction,
        dma: &dma::Handle<<RxTx<QUADSPI> as dma::Target>::Instance, state::Enabled>,
        stream: <RxTx<QUADSPI> as dma::Target>::Stream,
    ) -> Result<dma::Transfer<RxTx<QUADSPI>, B, dma::Started>, Error>
    where
        B: Deref + 'static,
        B::Target: AsSlice<Element = u8>,
    {
        // Only use DMA with data, for command only use `polling_write`
        match transaction.data_len {
            Some(data_len) => {
                assert!(
                    (data_len as u32) % 4 == 0,
                    "DMA transfer must be word aligned."
                );

                // Setup the transaction registers
                self.setup_transaction(QspiMode::INDIRECT_WRITE, &transaction);

                // Setup DMA transfer
                let tx_token = RxTx(PhantomData);
                let tx_transfer = unsafe {
                    dma::Transfer::new(
                        dma,
                        stream,
                        data,
                        tx_token,
                        self.dr_address(),
                        dma::Direction::MemoryToPeripheral,
                    )
                };

                let tx_transfer = tx_transfer.start(&dma);

                // Set DMA bit since we are using it
                self.qspi.cr.modify(|_, w| w.dmaen().set_bit());

                Ok(tx_transfer)
            }
            None => Err(Error::BadParam),
        }
    }

    /// Polling indirect read. Can also be used to perform transactions with no data.
    pub fn read(&mut self, buf: &mut [u8], transaction: QspiTransaction) -> Result<(), Error> {
        // Clear DMA bit since we are not using it
        self.qspi.cr.modify(|_, w| w.dmaen().clear_bit());

        // Setup the transaction registers
        self.setup_transaction(QspiMode::INDIRECT_READ, &transaction);

        // If the transaction has data, read it word-by-word from the data register
        if let Some(len) = transaction.data_len {
            let mut idx: usize = 0;
            while idx < len {
                // Check if there are bytes in the FIFO
                let num_bytes = self.qspi.sr.read().flevel().bits();
                if num_bytes > 0 {
                    // Read a word
                    let word = self.qspi.dr.read().data().bits();

                    // Unpack the word
                    let num_unpack = if num_bytes >= 4 { 4 } else { num_bytes };
                    for i in 0..num_unpack {
                        buf[idx] = ((word & (0xFF << (i * 8))) >> (i * 8)).try_into().unwrap();
                        idx += 1;
                    }
                }
            }
        }

        Ok(())
    }

    /// Polling indirect write.
    pub fn write(&mut self, buf: &[u8], transaction: QspiTransaction) -> Result<(), Error> {
        // Clear DMA bit since we are not using it
        self.qspi.cr.modify(|_, w| w.dmaen().clear_bit());

        // Setup the transaction registers
        self.setup_transaction(QspiMode::INDIRECT_WRITE, &transaction);

        // If the transaction has data, write it word-by-word to the data register
        if let Some(len) = transaction.data_len {
            let mut idx: usize = 0;
            while idx < len {
                // Check if the FIFO is empty
                let num_bytes = self.qspi.sr.read().flevel().bits();
                if num_bytes == 0 {
                    // Pack the word
                    let mut word: u32 = 0;
                    let num_pack = if (len - idx) >= 4 { 4 } else { len - idx };
                    for i in 0..num_pack {
                        word |= (buf[idx] as u32) << (i * 8);
                        idx += 1;
                    }

                    // Write a word
                    unsafe {
                        self.qspi.dr.write(|w| w.data().bits(word));
                    }
                }
            }
        }

        Ok(())
    }

    /// Map from QspiTransaction to QSPI registers.
    fn setup_transaction(&mut self, fmode: u8, transaction: &QspiTransaction) {
        unsafe {
            // Clear any prior status flags
            self.qspi.fcr.write(|w| w.bits(0x1B));

            // Update data length, if applicable
            if let Some(len) = transaction.data_len {
                self.qspi.dlr.write(|w| w.bits(len as u32 - 1));
            }

            // Update CCR register with metadata
            self.qspi.ccr.write_with_zero(|w| {
                w.fmode()
                    .bits(fmode)
                    .imode()
                    .bits(transaction.iwidth)
                    .admode()
                    .bits(transaction.awidth)
                    .dmode()
                    .bits(transaction.dwidth)
                    .adsize()
                    .bits(self.adsize)
                    .abmode()
                    .bits(QspiWidth::NONE)
                    .dcyc()
                    .bits(transaction.dummy)
                    .instruction()
                    .bits(transaction.instruction)
            });

            // Update address register, if applicable
            if let Some(addr) = transaction.address {
                self.qspi.ar.write(|w| w.bits(addr));
            }
        }
    }

    /// Get data register address.
    fn dr_address(&self) -> u32 {
        &self.qspi.dr as *const _ as _
    }
}

/// Token used for DMA transfers.
pub struct RxTx<I>(PhantomData<I>);