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}
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 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 pub const READ_STATUS2: u8 = 0x35;
174 pub const READ_STATUS3: u8 = 0x15;
175 pub const WRITE_STATUS: u8 = 0x01;
176 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 pub const LEGACY_PAGE_SIZE: u32 = 256;
189
190 pub const STATUS_WIP: u8 = 0x01;
193 pub const STATUS_WEL: u8 = 0x02;
195
196 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 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 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 pub fn wait_for_busy_clear(spi: &dyn Target) -> Result<()> {
235 spi.run_eeprom_transactions(&mut [Transaction::WaitForBusyClear])?;
236 Ok(())
237 }
238
239 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 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 pub fn read_sfdp(spi: &dyn Target) -> Result<Sfdp> {
257 let mut buf = vec![0u8; 256];
258 let mut tries = 0;
259 loop {
260 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 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 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 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 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 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 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 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 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 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 (
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 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 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 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 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 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 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 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 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 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 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}