opentitanlib/spiflash/
flash.rs

1// Copyright lowRISC contributors (OpenTitan project).
2// Licensed under the Apache License, Version 2.0, see LICENSE for details.
3// SPDX-License-Identifier: Apache-2.0
4
5use crate::app::NoProgressBar;
6use crate::io::eeprom::{AddressMode, MODE_111, MODE_112, MODE_114, Mode, Transaction};
7use crate::io::spi::Target;
8use crate::spiflash::sfdp::{
9    BlockEraseSize, FastReadParam, SectorErase, Sfdp, SupportedAddressModes,
10};
11use crate::transport::ProgressIndicator;
12use anyhow::{Result, ensure};
13use clap::ValueEnum;
14use std::convert::TryFrom;
15use thiserror::Error;
16
17#[derive(Debug, Error)]
18pub enum Error {
19    #[error("address out of bounds: {0} >= {1}")]
20    AddressOutOfBounds(u32, u32),
21    #[error("erase at address {0} not aligned on {1}-byte boundary")]
22    BadEraseAddress(u32, u32),
23    #[error("erase length {0} not a multiple of {1} bytes")]
24    BadEraseLength(u32, u32),
25    #[error("bad sequence length: {0}")]
26    BadSequenceLength(usize),
27    #[error("unsupported mode: {0:?}")]
28    UnsupportedMode(ReadMode),
29    #[error("unsupported opcode: {0:x?}")]
30    UnsupportedOpcode(u8),
31}
32
33impl From<SupportedAddressModes> for AddressMode {
34    fn from(mode: SupportedAddressModes) -> Self {
35        match mode {
36            SupportedAddressModes::Mode4b => AddressMode::Mode4b,
37            _ => AddressMode::Mode3b,
38        }
39    }
40}
41
42#[derive(Debug, Clone, Copy, ValueEnum, Default)]
43#[value(rename_all = "verbatim")]
44pub enum ReadMode {
45    #[default]
46    Standard,
47    Fast,
48    Dual,
49    Quad,
50}
51
52pub struct ReadTypes {
53    pub standard: FastReadParam,
54    pub fast: FastReadParam,
55    pub dual: FastReadParam,
56    pub quad: FastReadParam,
57}
58
59impl Default for ReadTypes {
60    fn default() -> Self {
61        Self {
62            standard: FastReadParam {
63                wait_states: 0,
64                mode_bits: 0,
65                opcode: SpiFlash::READ,
66            },
67            fast: FastReadParam {
68                wait_states: 8,
69                mode_bits: 0,
70                opcode: SpiFlash::FAST_READ,
71            },
72            dual: FastReadParam {
73                wait_states: 8,
74                mode_bits: 0,
75                opcode: SpiFlash::FAST_DUAL_READ,
76            },
77            quad: FastReadParam {
78                wait_states: 8,
79                mode_bits: 0,
80                opcode: SpiFlash::FAST_QUAD_READ,
81            },
82        }
83    }
84}
85
86impl ReadTypes {
87    pub fn from_sfdp(sfdp: &Sfdp) -> Self {
88        Self {
89            standard: FastReadParam {
90                wait_states: 0,
91                mode_bits: 0,
92                opcode: SpiFlash::READ,
93            },
94            fast: FastReadParam {
95                wait_states: 8,
96                mode_bits: 0,
97                opcode: SpiFlash::FAST_READ,
98            },
99            dual: if sfdp.jedec.support_fast_read_112 {
100                sfdp.jedec.param_112.clone()
101            } else {
102                Default::default()
103            },
104            quad: if sfdp.jedec.support_fast_read_114 {
105                sfdp.jedec.param_114.clone()
106            } else {
107                Default::default()
108            },
109        }
110    }
111}
112
113#[derive(Debug, Clone, Copy, PartialEq, Eq, ValueEnum, Default)]
114#[value(rename_all = "verbatim")]
115pub enum EraseMode {
116    #[default]
117    Standard,
118    Block,
119}
120
121pub struct SpiFlash {
122    pub size: u32,
123    pub program_size: u32,
124    pub address_mode: AddressMode,
125    pub read_mode: ReadMode,
126    pub erase_mode: EraseMode,
127    pub sfdp: Option<Sfdp>,
128    pub read_type: ReadTypes,
129    pub erase: Vec<SectorErase>,
130    pub program_ff_optimization: bool,
131}
132
133impl Default for SpiFlash {
134    fn default() -> Self {
135        SpiFlash {
136            size: 16 * 1024 * 1024,
137            program_size: SpiFlash::LEGACY_PAGE_SIZE,
138            address_mode: Default::default(),
139            read_mode: Default::default(),
140            erase_mode: Default::default(),
141            sfdp: None,
142            read_type: Default::default(),
143            erase: vec![SectorErase {
144                size: 4096,
145                opcode: SpiFlash::SECTOR_ERASE,
146                time: None,
147            }],
148            program_ff_optimization: true,
149        }
150    }
151}
152
153impl SpiFlash {
154    // Well known SPI Flash opcodes.
155    pub const READ: u8 = 0x03;
156    pub const FAST_READ: u8 = 0x0b;
157    pub const FAST_DUAL_READ: u8 = 0x3b;
158    pub const FAST_QUAD_READ: u8 = 0x6b;
159    pub const READ_4B: u8 = 0x13;
160    pub const FAST_READ_4B: u8 = 0x0c;
161    pub const FAST_DUAL_READ_4B: u8 = 0x3c;
162    pub const FAST_QUAD_READ_4B: u8 = 0x6c;
163    pub const PAGE_PROGRAM: u8 = 0x02;
164    pub const SECTOR_ERASE: u8 = 0x20;
165    pub const BLOCK_ERASE_32K: u8 = 0x52;
166    pub const BLOCK_ERASE_64K: u8 = 0xD8;
167    pub const SECTOR_ERASE_4B: u8 = 0x21;
168    pub const BLOCK_ERASE_32K_4B: u8 = 0x5C;
169    pub const BLOCK_ERASE_64K_4B: u8 = 0xDC;
170    pub const CHIP_ERASE: u8 = 0xc7;
171    pub const WRITE_ENABLE: u8 = 0x06;
172    pub const WRITE_DISABLE: u8 = 0x04;
173    pub const READ_STATUS: u8 = 0x05;
174    // Winbond parts use 0x35 and 0x15 for extended status reads.
175    pub const READ_STATUS2: u8 = 0x35;
176    pub const READ_STATUS3: u8 = 0x15;
177    pub const WRITE_STATUS: u8 = 0x01;
178    // Winbond parts use 0x31 and 0x11 for extended status writes.
179    pub const WRITE_STATUS2: u8 = 0x31;
180    pub const WRITE_STATUS3: u8 = 0x11;
181    pub const READ_ID: u8 = 0x9f;
182    pub const ENTER_4B: u8 = 0xb7;
183    pub const EXIT_4B: u8 = 0xe9;
184    pub const READ_SFDP: u8 = 0x5a;
185    pub const NOP: u8 = 0x00;
186    pub const RESET_ENABLE: u8 = 0x66;
187    pub const RESET: u8 = 0x99;
188
189    /// The legacy JEDEC page size for programming operations is 256 bytes.
190    pub const LEGACY_PAGE_SIZE: u32 = 256;
191
192    /// Status register bits:
193    /// The `WIP` bit indicates a write in progress (sometimes called the busy bit).
194    pub const STATUS_WIP: u8 = 0x01;
195    /// The `WEL` bit is the write enable latch.
196    pub const STATUS_WEL: u8 = 0x02;
197
198    /// Read `length` bytes of the JEDEC ID from the `spi` target.
199    pub fn read_jedec_id(spi: &dyn Target, length: usize) -> Result<Vec<u8>> {
200        let mut buf = vec![0u8; length];
201        spi.run_eeprom_transactions(&mut [Transaction::Read(
202            MODE_111.cmd(SpiFlash::READ_ID),
203            &mut buf,
204        )])?;
205        Ok(buf)
206    }
207
208    /// Read status register from the `spi` target.
209    pub fn read_status(spi: &dyn Target) -> Result<u8> {
210        let mut buf = [0u8; 1];
211        spi.run_eeprom_transactions(&mut [Transaction::Read(
212            MODE_111.cmd(SpiFlash::READ_STATUS),
213            &mut buf,
214        )])?;
215        Ok(buf[0])
216    }
217
218    /// Read the extended status register from the `spi` target.
219    pub fn read_status_ex(spi: &dyn Target, seq: Option<&[u8]>) -> Result<u32> {
220        let seq = seq.unwrap_or(&[Self::READ_STATUS, Self::READ_STATUS2, Self::READ_STATUS3]);
221        ensure!(
222            !seq.is_empty() && seq.len() <= 3,
223            Error::BadSequenceLength(seq.len())
224        );
225        let mut buf = [0u8; 4];
226        for (op, byte) in seq.iter().zip(buf.iter_mut()) {
227            spi.run_eeprom_transactions(&mut [Transaction::Read(
228                MODE_111.cmd(*op),
229                std::slice::from_mut(byte),
230            )])?;
231        }
232        Ok(u32::from_le_bytes(buf))
233    }
234
235    /// Poll the status register waiting for the busy bit to clear.
236    pub fn wait_for_busy_clear(spi: &dyn Target) -> Result<()> {
237        spi.run_eeprom_transactions(&mut [Transaction::WaitForBusyClear])?;
238        Ok(())
239    }
240
241    /// Send the WRITE_ENABLE opcode to the `spi` target.
242    pub fn set_write_enable(spi: &dyn Target) -> Result<()> {
243        spi.run_eeprom_transactions(&mut [Transaction::Command(
244            MODE_111.cmd(SpiFlash::WRITE_ENABLE),
245        )])?;
246        Ok(())
247    }
248
249    /// Send the WRITE_DISABLE opcode to the `spi` target.
250    pub fn set_write_disable(spi: &dyn Target) -> Result<()> {
251        spi.run_eeprom_transactions(&mut [Transaction::Command(
252            MODE_111.cmd(SpiFlash::WRITE_DISABLE),
253        )])?;
254        Ok(())
255    }
256
257    /// Read and parse the SFDP table from the `spi` target.
258    pub fn read_sfdp(spi: &dyn Target) -> Result<Sfdp> {
259        let mut buf = vec![0u8; 256];
260        let mut tries = 0;
261        loop {
262            // READ_SFDP always takes a 3-byte address followed by a dummy byte regardless of
263            // address mode.
264            let mut eeprom_transactions = Vec::new();
265            let read_size = spi.get_eeprom_max_transfer_sizes()?.read;
266            for (i, transfer) in buf.chunks_mut(read_size).enumerate() {
267                eeprom_transactions.push(Transaction::Read(
268                    MODE_111.dummy_cycles(8).cmd_addr(
269                        SpiFlash::READ_SFDP,
270                        (i * read_size) as u32,
271                        AddressMode::Mode3b,
272                    ),
273                    transfer,
274                ));
275            }
276            spi.run_eeprom_transactions(&mut eeprom_transactions)?;
277
278            // We only want to give SFDP parsing one extra chance for length
279            // extension. If parsing fails a second time, just return the error.
280            let result = Sfdp::try_from(&buf[..]);
281            if result.is_ok() || tries > 0 {
282                return result;
283            }
284            buf.resize(Sfdp::length_required(&buf)?, 0);
285            tries += 1;
286        }
287    }
288
289    /// Create a new `SpiFlash` instance from an SFDP table.
290    pub fn from_sfdp(sfdp: Sfdp) -> Self {
291        let read_type = ReadTypes::from_sfdp(&sfdp);
292        let mut erase = sfdp
293            .jedec
294            .erase
295            .iter()
296            .filter_map(|e| if e.size != 0 { Some(e.clone()) } else { None })
297            .collect::<Vec<_>>();
298        // If the SFDP claims to support 4K erases, but a 4K SectorErase is not
299        // present, synthesize one and add it to the list.
300        if sfdp.jedec.block_erase_size == BlockEraseSize::Block4KiB
301            && !erase.iter().any(|e| e.size == 4096)
302        {
303            erase.push(SectorErase {
304                size: 4096,
305                opcode: sfdp.jedec.erase_opcode_4kib,
306                time: None,
307            });
308        }
309        // Sort largest to smallest.
310        erase.sort_by(|a, b| b.size.cmp(&a.size));
311
312        SpiFlash {
313            size: sfdp.jedec.density,
314            program_size: SpiFlash::LEGACY_PAGE_SIZE,
315            address_mode: AddressMode::from(sfdp.jedec.address_modes),
316            read_mode: Default::default(),
317            erase_mode: Default::default(),
318            sfdp: Some(sfdp),
319            read_type,
320            erase,
321            program_ff_optimization: true,
322        }
323    }
324
325    /// Create a new `SpiFlash` instance by reading an SFDP table from the `spi` Target.
326    pub fn from_spi(spi: &dyn Target) -> Result<Self> {
327        let sfdp = SpiFlash::read_sfdp(spi)?;
328        Ok(SpiFlash::from_sfdp(sfdp))
329    }
330
331    /// Set the SPI flash addressing mode to either 3b or 4b mode.
332    pub fn set_address_mode(&mut self, spi: &dyn Target, mode: AddressMode) -> Result<()> {
333        let opcode = match mode {
334            AddressMode::Mode3b => SpiFlash::EXIT_4B,
335            AddressMode::Mode4b => SpiFlash::ENTER_4B,
336        };
337        spi.run_eeprom_transactions(&mut [Transaction::Command(MODE_111.cmd(opcode))])?;
338        self.address_mode = mode;
339        Ok(())
340    }
341
342    /// Automatically set the addressing mode based on the size of the SPI flash.
343    pub fn set_address_mode_auto(&mut self, spi: &dyn Target) -> Result<()> {
344        self.set_address_mode(
345            spi,
346            if self.size <= 16 * 1024 * 1024 {
347                AddressMode::Mode3b
348            } else {
349                AddressMode::Mode4b
350            },
351        )
352    }
353
354    /// Read into `buffer` from the SPI flash starting at `address`.
355    pub fn read(&self, spi: &dyn Target, address: u32, buffer: &mut [u8]) -> Result<&Self> {
356        self.read_with_progress(spi, address, buffer, &NoProgressBar, false)
357    }
358
359    fn select_read(&self) -> Result<(Mode, &FastReadParam)> {
360        match self.read_mode {
361            ReadMode::Standard => Ok((MODE_111, &self.read_type.standard)),
362            ReadMode::Fast => Ok((MODE_111, &self.read_type.fast)),
363            ReadMode::Dual if self.read_type.dual.opcode != 0 => {
364                Ok((MODE_112, &self.read_type.dual))
365            }
366            ReadMode::Quad if self.read_type.quad.opcode != 0 => {
367                Ok((MODE_114, &self.read_type.quad))
368            }
369            _ => Err(Error::UnsupportedMode(self.read_mode).into()),
370        }
371    }
372
373    /// Read into `buffer` from the SPI flash starting at `address`.
374    /// The `progress` callback will be invoked after each chunk of the read operation.
375    pub fn read_with_progress(
376        &self,
377        spi: &dyn Target,
378        mut address: u32,
379        buffer: &mut [u8],
380        progress: &dyn ProgressIndicator,
381        use_4b_opcodes: bool,
382    ) -> Result<&Self> {
383        progress.new_stage("", buffer.len());
384        let (mode, param) = self.select_read()?;
385        let (address_mode, opcode) = if use_4b_opcodes {
386            // If we've been asked to use the 4-byte version of the read opcodes,
387            // map the opcode to its 4b counterpart.
388            (
389                AddressMode::Mode4b,
390                match param.opcode {
391                    SpiFlash::READ => SpiFlash::READ_4B,
392                    SpiFlash::FAST_READ => SpiFlash::FAST_READ_4B,
393                    SpiFlash::FAST_DUAL_READ => SpiFlash::FAST_DUAL_READ_4B,
394                    SpiFlash::FAST_QUAD_READ => SpiFlash::FAST_QUAD_READ_4B,
395                    _ => return Err(Error::UnsupportedOpcode(param.opcode).into()),
396                },
397            )
398        } else {
399            (self.address_mode, param.opcode)
400        };
401        // Break the read up according to the maximum chunksize the backend can handle.
402        let chunk_size = spi.get_eeprom_max_transfer_sizes()?.read;
403        for (idx, chunk) in buffer.chunks_mut(chunk_size).enumerate() {
404            progress.progress(idx * chunk_size);
405            spi.run_eeprom_transactions(&mut [Transaction::Read(
406                mode.dummy_cycles(param.wait_states)
407                    .cmd_addr(opcode, address, address_mode),
408                chunk,
409            )])?;
410            address += chunk.len() as u32;
411        }
412        progress.progress(buffer.len());
413        Ok(self)
414    }
415
416    /// Erase the entire EEPROM via the CHIP_ERASE opcode.
417    pub fn chip_erase(&self, spi: &dyn Target) -> Result<&Self> {
418        spi.run_eeprom_transactions(&mut [
419            Transaction::Command(MODE_111.cmd(SpiFlash::WRITE_ENABLE)),
420            Transaction::Command(MODE_111.cmd(SpiFlash::CHIP_ERASE)),
421            Transaction::WaitForBusyClear,
422        ])?;
423        Ok(self)
424    }
425
426    /// Erase a segment of the SPI flash starting at `address` for `length` bytes.
427    /// The address and length must be sector aligned.
428    pub fn erase(&self, spi: &dyn Target, address: u32, length: u32) -> Result<&Self> {
429        self.erase_with_progress(spi, address, length, &NoProgressBar)
430    }
431
432    fn select_erase(&self, address: u32, length: u32) -> Result<&SectorErase> {
433        if self.erase_mode == EraseMode::Standard {
434            // We assume the last element of the `erase` list is the standard
435            // SECTOR_ERASE.  So far, this has been true for all eeproms
436            // encountered by the author.
437            return Ok(self.erase.last().unwrap());
438        }
439        for e in self.erase.iter() {
440            if address.is_multiple_of(e.size) && length >= e.size {
441                return Ok(e);
442            }
443        }
444        Err(Error::BadEraseAddress(address, self.erase.last().unwrap().size).into())
445    }
446
447    /// Erase a segment of the SPI flash starting at `address` for `length` bytes.
448    /// The address and length must be sector aligned.
449    /// The `progress` callback will be invoked after each chunk of the erase operation.
450    pub fn erase_with_progress(
451        &self,
452        spi: &dyn Target,
453        address: u32,
454        length: u32,
455        progress: &dyn ProgressIndicator,
456    ) -> Result<&Self> {
457        let min_erase_size = self.erase.last().unwrap().size;
458        if !address.is_multiple_of(min_erase_size) {
459            return Err(Error::BadEraseAddress(address, min_erase_size).into());
460        }
461        if !length.is_multiple_of(min_erase_size) {
462            return Err(Error::BadEraseLength(length, min_erase_size).into());
463        }
464        progress.new_stage("", length as usize);
465        let end = address + length;
466        let mut addr = address;
467        while addr < end {
468            let erase = self.select_erase(addr, end - addr)?;
469            spi.run_eeprom_transactions(&mut [
470                Transaction::Command(MODE_111.cmd(SpiFlash::WRITE_ENABLE)),
471                Transaction::Command(MODE_111.cmd_addr(erase.opcode, addr, self.address_mode)),
472                Transaction::WaitForBusyClear,
473            ])?;
474            progress.progress((addr - address) as usize);
475            addr += erase.size;
476        }
477        progress.progress(length as usize);
478        Ok(self)
479    }
480
481    /// Program a segment of the SPI flash starting at `address` with the contents of `buffer`.
482    /// The address and buffer length may be arbitrary.  This function will not
483    /// erase the segment first.
484    pub fn program(&self, spi: &dyn Target, address: u32, buffer: &[u8]) -> Result<&Self> {
485        self.program_with_progress(spi, address, buffer, &NoProgressBar)
486    }
487
488    /// Program a segment of the SPI flash starting at `address` with the contents of `buffer`.
489    /// The address and buffer length may be arbitrary.  This function will not
490    /// erase the segment first.
491    /// The `progress` callback will be invoked after each chunk of the program operation.
492    pub fn program_with_progress(
493        &self,
494        spi: &dyn Target,
495        mut address: u32,
496        buffer: &[u8],
497        progress: &dyn ProgressIndicator,
498    ) -> Result<&Self> {
499        progress.new_stage("", buffer.len());
500        let mut remain = buffer.len();
501        let mut chunk_start = 0usize;
502        while remain != 0 {
503            // If the address isn't program-page-size aligned, adjust the first
504            // chunk so that subsequent writes will be so-aligned.
505            // This is necessary because the SPI eeprom will wrap within the
506            // programming page and the resultant data in the eeprom will not
507            // be what you intended.
508            let chunk_size = (self.program_size - (address % self.program_size)) as usize;
509            let chunk_size = std::cmp::min(chunk_size, remain);
510            let chunk_end = chunk_start + chunk_size;
511            let chunk = &buffer[chunk_start..chunk_end];
512            // Skip this chunk if all bytes are 0xff.
513            if !self.program_ff_optimization || !chunk.iter().all(|&x| x == 0xff) {
514                spi.run_eeprom_transactions(&mut [
515                    Transaction::Command(MODE_111.cmd(SpiFlash::WRITE_ENABLE)),
516                    Transaction::Write(
517                        MODE_111.cmd_addr(SpiFlash::PAGE_PROGRAM, address, self.address_mode),
518                        chunk,
519                    ),
520                    Transaction::WaitForBusyClear,
521                ])?;
522            }
523            address += chunk_size as u32;
524            chunk_start += chunk_size;
525            remain -= chunk_size;
526            progress.progress(buffer.len() - remain);
527        }
528        Ok(self)
529    }
530
531    /// Send the software reset sequence to the `spi` target.
532    pub fn chip_reset(spi: &dyn Target) -> Result<()> {
533        spi.run_eeprom_transactions(&mut [
534            Transaction::Command(MODE_111.cmd(SpiFlash::RESET_ENABLE)),
535            Transaction::Command(MODE_111.cmd(SpiFlash::RESET)),
536        ])?;
537        Ok(())
538    }
539}