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