1use 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 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 pub const READ_STATUS2: u8 = 0x35;
176 pub const READ_STATUS3: u8 = 0x15;
177 pub const WRITE_STATUS: u8 = 0x01;
178 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 pub const LEGACY_PAGE_SIZE: u32 = 256;
191
192 pub const STATUS_WIP: u8 = 0x01;
195 pub const STATUS_WEL: u8 = 0x02;
197
198 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 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 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 pub fn wait_for_busy_clear(spi: &dyn Target) -> Result<()> {
237 spi.run_eeprom_transactions(&mut [Transaction::WaitForBusyClear])?;
238 Ok(())
239 }
240
241 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 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 pub fn read_sfdp(spi: &dyn Target) -> Result<Sfdp> {
259 let mut buf = vec![0u8; 256];
260 let mut tries = 0;
261 loop {
262 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 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 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 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 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 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 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 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 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 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 (
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 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 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 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 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 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 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 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 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 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 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}