1use crate::{utils::HexSlice, Error};
4use bitflags::bitflags;
5use core::convert::TryInto;
6use core::fmt;
7use embedded_hal::blocking::{delay::DelayUs, spi::Transfer};
8use embedded_hal::digital::v2::OutputPin;
9
10pub struct Identification {
12 bytes: [u8; 3],
16
17 continuations: u8,
19}
20
21impl Identification {
22 pub fn from_jedec_id(buf: &[u8]) -> Identification {
24 let mut start_idx = 0;
31 for i in 0..(buf.len() - 2) {
32 if buf[i] != 0x7F {
33 start_idx = i;
34 break;
35 }
36 }
37
38 Self {
39 bytes: [buf[start_idx], buf[start_idx + 1], buf[start_idx + 2]],
40 continuations: start_idx as u8,
41 }
42 }
43
44 pub fn mfr_code(&self) -> u8 {
46 self.bytes[0]
47 }
48
49 pub fn device_id(&self) -> &[u8] {
51 self.bytes[1..].as_ref()
52 }
53
54 pub fn continuation_count(&self) -> u8 {
59 self.continuations
60 }
61}
62
63impl fmt::Debug for Identification {
64 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
65 f.debug_tuple("Identification")
66 .field(&HexSlice(self.bytes))
67 .finish()
68 }
69}
70
71#[repr(u8)]
72#[allow(unused)] enum Opcode {
74 ReadDeviceId = 0xAB,
76 ReadMfDId = 0x90,
78 ReadJedecId = 0x9F,
80 WriteEnable = 0x06,
82 WriteDisable = 0x04,
84 ReadStatus = 0x05,
86 WriteStatus = 0x01,
88 Read = 0x03,
89 PageProg = 0x02, SectorErase = 0x20,
91 BlockErase = 0xD8,
92 ChipErase = 0xC7,
93 PowerDown = 0xB9,
94}
95
96bitflags! {
97 pub struct Status: u8 {
99 const BUSY = 1 << 0;
101 const WEL = 1 << 1;
103 const PROT = 0b00011100;
105 const SRWD = 1 << 7;
107 }
108}
109
110pub struct FlashInfo {
111 pub id: u32,
112 pub page_size: u16,
113 pub sector_size: u32,
114 pub page_count: u32,
115 pub sector_count: u32,
116 pub block_size: u32,
117 pub block_count: u32,
118 pub capacity_kb: u32,
119}
120
121impl fmt::Debug for FlashInfo {
122 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
123 f.debug_tuple("FlashInfo")
124 .field(&self.id)
125 .field(&format_args!("_KB:_"))
126 .field(&self.capacity_kb)
127 .finish()
128 }
129}
130
131#[derive(Debug)]
139pub struct Flash<SPI, CS: OutputPin> {
140 spi: SPI,
141 cs: CS,
142}
143
144impl<SPI: Transfer<u8>, CS: OutputPin> Flash<SPI, CS> {
147 pub fn init(spi: SPI, cs: CS) -> Result<Self, Error<SPI, CS>> {
156 let mut this = Self { cs, spi };
157
158 let status = this.read_status()?;
159 info!("Flash::init: status = {:?}", status);
160
161 if !(status & (Status::BUSY | Status::WEL)).is_empty() {
164 return Err(Error::UnexpectedStatus);
165 }
166
167 Ok(this)
168 }
169
170 fn command(&mut self, bytes: &mut [u8]) -> Result<(), Error<SPI, CS>> {
171 self.cs.set_low().map_err(Error::Gpio)?;
173 let spi_result = self.spi.transfer(bytes).map_err(Error::Spi);
174 self.cs.set_high().map_err(Error::Gpio)?;
175 spi_result?;
176 Ok(())
177 }
178
179 pub fn read_jedec_id(&mut self) -> Result<Identification, Error<SPI, CS>> {
181 let mut buf: [u8; 12] = [0; 12];
183 buf[0] = Opcode::ReadJedecId as u8;
184 self.command(&mut buf)?;
185
186 Ok(Identification::from_jedec_id(&buf[1..]))
188 }
189
190 pub fn read_status(&mut self) -> Result<Status, Error<SPI, CS>> {
192 let mut buf = [Opcode::ReadStatus as u8, 0];
193 self.command(&mut buf)?;
194
195 Ok(Status::from_bits_truncate(buf[1]))
196 }
197
198 pub fn get_device_info(&mut self) -> Result<FlashInfo, Error<SPI, CS>> {
199 let mut buf: [u8; 12] = [0; 12];
200 buf[0] = Opcode::ReadJedecId as u8;
201 self.command(&mut buf)?;
202
203 let full_id: u32 =
204 (((buf[1] as u32) << 16) | ((buf[2] as u32) << 8) | (buf[3] as u32)) & 0x000000FF;
205
206 let block_count = match full_id {
207 0x20 => 1024, 0x19 => 512, 0x18 => 256, 0x17 => 128, 0x16 => 64, 0x15 => 32, 0x14 => 16, 0x13 => 8, 0x12 => 4, 0x11 => 2, 33_u32..=u32::MAX => 0,
218 0_u32..=16_u32 => 0,
219 26_u32..=31_u32 => 0,
220 };
221
222 let device_info = FlashInfo {
223 id: 0,
224 page_size: 256,
225 sector_size: 0x1000,
226 sector_count: block_count * 16,
227 page_count: (block_count * 16 * 0x1000) / 256,
228 block_size: 0x1000 * 16,
229 block_count,
230 capacity_kb: (0x1000 * 16 * block_count) / 1024,
231 };
232 return Ok(device_info);
233 }
234
235 pub fn write_enable(&mut self) -> Result<(), Error<SPI, CS>> {
236 let mut cmd_buf = [Opcode::WriteEnable as u8];
237 self.command(&mut cmd_buf)?;
238 Ok(())
239 }
240
241 fn wait_done(&mut self) -> Result<(), Error<SPI, CS>> {
242 while self.read_status()?.contains(Status::BUSY) {}
244 Ok(())
245 }
246
247 pub fn power_down(&mut self) -> Result<(), Error<SPI, CS>> {
264 let mut buf = [Opcode::PowerDown as u8];
265 self.command(&mut buf)?;
266
267 Ok(())
268 }
269
270 pub fn release_power_down<D: DelayUs<u8>>(
282 &mut self,
283 delay: &mut D,
284 ) -> Result<(), Error<SPI, CS>> {
285 let mut buf = [Opcode::ReadDeviceId as u8];
287 self.command(&mut buf)?;
288
289 delay.delay_us(6); Ok(())
292 }
293
294 pub fn read(&mut self, addr: u32, buf: &mut [u8]) -> Result<(), Error<SPI, CS>> {
307 let mut cmd_buf = [
310 Opcode::Read as u8,
311 (addr >> 16) as u8,
312 (addr >> 8) as u8,
313 addr as u8,
314 ];
315
316 self.cs.set_low().map_err(Error::Gpio)?;
317 let mut spi_result = self.spi.transfer(&mut cmd_buf);
318 if spi_result.is_ok() {
319 spi_result = self.spi.transfer(buf);
320 }
321 self.cs.set_high().map_err(Error::Gpio)?;
322 spi_result.map(|_| ()).map_err(Error::Spi)
323 }
324
325 pub fn erase_sectors(&mut self, addr: u32, amount: usize) -> Result<(), Error<SPI, CS>> {
326 for c in 0..amount {
327 self.write_enable()?;
328
329 let current_addr: u32 = (addr as usize + c * 256).try_into().unwrap();
330 let mut cmd_buf = [
331 Opcode::SectorErase as u8,
332 (current_addr >> 16) as u8,
333 (current_addr >> 8) as u8,
334 current_addr as u8,
335 ];
336 self.command(&mut cmd_buf)?;
337 self.wait_done()?;
338 }
339
340 Ok(())
341 }
342
343 pub fn write_bytes(&mut self, addr: u32, data: &mut [u8]) -> Result<(), Error<SPI, CS>> {
344 for (c, chunk) in data.chunks_mut(256).enumerate() {
345 self.write_enable()?;
346
347 let current_addr: u32 = (addr as usize + c * 256).try_into().unwrap();
348 let mut cmd_buf = [
349 Opcode::PageProg as u8,
350 (current_addr >> 16) as u8,
351 (current_addr >> 8) as u8,
352 current_addr as u8,
353 ];
354
355 self.cs.set_low().map_err(Error::Gpio)?;
356 let mut spi_result = self.spi.transfer(&mut cmd_buf);
357 if spi_result.is_ok() {
358 spi_result = self.spi.transfer(chunk);
359 }
360 self.cs.set_high().map_err(Error::Gpio)?;
361 spi_result.map(|_| ()).map_err(Error::Spi)?;
362 self.wait_done()?;
363 }
364 Ok(())
365 }
366
367 pub fn erase_block(&mut self, addr: u32) -> Result<(), Error<SPI, CS>> {
368 self.write_enable()?;
369
370 let mut cmd_buf = [
371 Opcode::BlockErase as u8,
372 (addr >> 16) as u8,
373 (addr >> 8) as u8,
374 addr as u8,
375 ];
376 self.command(&mut cmd_buf)?;
377 self.wait_done()
378 }
379
380 pub fn erase_all(&mut self) -> Result<(), Error<SPI, CS>> {
381 self.write_enable()?;
382 let mut cmd_buf = [Opcode::ChipErase as u8];
383 self.command(&mut cmd_buf)?;
384 self.wait_done()?;
385 Ok(())
386 }
387}
388
389impl FlashInfo {
390 pub fn page_to_sector(&self, page_address: &u32) -> u32 {
391 return (page_address * (self.page_size) as u32) / self.sector_size;
392 }
393
394 pub fn page_to_block(&self, page_address: &u32) -> u32 {
395 return (page_address * (self.page_size) as u32) / self.block_size;
396 }
397
398 pub fn sector_to_block(&self, sector_address: &u32) -> u32 {
399 return (sector_address * (self.sector_size) as u32) / self.block_size;
400 }
401
402 pub fn sector_to_page(&self, sector_address: &u32) -> u32 {
403 return (sector_address * (self.sector_size) as u32) / (self.page_size) as u32;
404 }
405
406 pub fn block_to_page(&self, block_adress: &u32) -> u32 {
407 return (block_adress * (self.block_size) as u32) / (self.page_size) as u32;
408 }
409}
410
411#[cfg(test)]
412mod tests {
413 use super::*;
414
415 #[test]
416 fn test_decode_jedec_id() {
417 let cypress_id_bytes = [0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0xC2, 0x22, 0x08];
418 let ident = Identification::from_jedec_id(&cypress_id_bytes);
419 assert_eq!(0xC2, ident.mfr_code());
420 assert_eq!(6, ident.continuation_count());
421 let device_id = ident.device_id();
422 assert_eq!(device_id[0], 0x22);
423 assert_eq!(device_id[1], 0x08);
424 }
425}