1use crate::{utils::HexSlice, BlockDevice, Error, Read};
4use bitflags::bitflags;
5use core::convert::TryInto;
6use core::fmt;
7use embedded_hal::blocking::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#[allow(unused)] enum Opcode {
73 ReadDeviceId = 0xAB,
75 ReadMfDId = 0x90,
77 ReadJedecId = 0x9F,
79 WriteEnable = 0x06,
81 WriteDisable = 0x04,
83 ReadStatus = 0x05,
85 WriteStatus = 0x01,
87 Read = 0x03,
88 PageProg = 0x02, SectorErase = 0x20,
90 BlockErase = 0xD8,
91 ChipErase = 0xC7,
92}
93
94bitflags! {
95 pub struct Status: u8 {
97 const BUSY = 1 << 0;
99 const WEL = 1 << 1;
101 const PROT = 0b00011100;
103 const SRWD = 1 << 7;
105 }
106}
107
108#[derive(Debug)]
116pub struct Flash<SPI: Transfer<u8>, CS: OutputPin> {
117 spi: SPI,
118 cs: CS,
119}
120
121impl<SPI: Transfer<u8>, CS: OutputPin> Flash<SPI, CS> {
122 pub fn init(spi: SPI, cs: CS) -> Result<Self, Error<SPI, CS>> {
131 let mut this = Self { spi, cs };
132 let status = this.read_status()?;
133 info!("Flash::init: status = {:?}", status);
134
135 if !(status & (Status::BUSY | Status::WEL)).is_empty() {
138 return Err(Error::UnexpectedStatus);
139 }
140
141 Ok(this)
142 }
143
144 fn command(&mut self, bytes: &mut [u8]) -> Result<(), Error<SPI, CS>> {
145 self.cs.set_low().map_err(Error::Gpio)?;
147 let spi_result = self.spi.transfer(bytes).map_err(Error::Spi);
148 self.cs.set_high().map_err(Error::Gpio)?;
149 spi_result?;
150 Ok(())
151 }
152
153 pub fn read_jedec_id(&mut self) -> Result<Identification, Error<SPI, CS>> {
155 let mut buf: [u8; 12] = [0; 12];
157 buf[0] = Opcode::ReadJedecId as u8;
158 self.command(&mut buf)?;
159
160 Ok(Identification::from_jedec_id(&buf[1..]))
162 }
163
164 pub fn read_status(&mut self) -> Result<Status, Error<SPI, CS>> {
166 let mut buf = [Opcode::ReadStatus as u8, 0];
167 self.command(&mut buf)?;
168
169 Ok(Status::from_bits_truncate(buf[1]))
170 }
171
172 fn write_enable(&mut self) -> Result<(), Error<SPI, CS>> {
173 let mut cmd_buf = [Opcode::WriteEnable as u8];
174 self.command(&mut cmd_buf)?;
175 Ok(())
176 }
177
178 fn wait_done(&mut self) -> Result<(), Error<SPI, CS>> {
179 while self.read_status()?.contains(Status::BUSY) {}
181 Ok(())
182 }
183}
184
185impl<SPI: Transfer<u8>, CS: OutputPin> Read<u32, SPI, CS> for Flash<SPI, CS> {
186 fn read(&mut self, addr: u32, buf: &mut [u8]) -> Result<(), Error<SPI, CS>> {
199 let mut cmd_buf = [
202 Opcode::Read as u8,
203 (addr >> 16) as u8,
204 (addr >> 8) as u8,
205 addr as u8,
206 ];
207
208 self.cs.set_low().map_err(Error::Gpio)?;
209 let mut spi_result = self.spi.transfer(&mut cmd_buf);
210 if spi_result.is_ok() {
211 spi_result = self.spi.transfer(buf);
212 }
213 self.cs.set_high().map_err(Error::Gpio)?;
214 spi_result.map(|_| ()).map_err(Error::Spi)
215 }
216}
217
218impl<SPI: Transfer<u8>, CS: OutputPin> BlockDevice<u32, SPI, CS> for Flash<SPI, CS> {
219 fn erase_sectors(&mut self, addr: u32, amount: usize) -> Result<(), Error<SPI, CS>> {
220 for c in 0..amount {
221 self.write_enable()?;
222
223 let current_addr: u32 = (addr as usize + c * 256).try_into().unwrap();
224 let mut cmd_buf = [
225 Opcode::SectorErase as u8,
226 (current_addr >> 16) as u8,
227 (current_addr >> 8) as u8,
228 current_addr as u8,
229 ];
230 self.command(&mut cmd_buf)?;
231 self.wait_done()?;
232 }
233
234 Ok(())
235 }
236
237 fn write_bytes(&mut self, addr: u32, data: &mut [u8]) -> Result<(), Error<SPI, CS>> {
238 for (c, chunk) in data.chunks_mut(256).enumerate() {
239 self.write_enable()?;
240
241 let current_addr: u32 = (addr as usize + c * 256).try_into().unwrap();
242 let mut cmd_buf = [
243 Opcode::PageProg as u8,
244 (current_addr >> 16) as u8,
245 (current_addr >> 8) as u8,
246 current_addr as u8,
247 ];
248
249 self.cs.set_low().map_err(Error::Gpio)?;
250 let mut spi_result = self.spi.transfer(&mut cmd_buf);
251 if spi_result.is_ok() {
252 spi_result = self.spi.transfer(chunk);
253 }
254 self.cs.set_high().map_err(Error::Gpio)?;
255 spi_result.map(|_| ()).map_err(Error::Spi)?;
256 self.wait_done()?;
257 }
258 Ok(())
259 }
260
261 fn erase_all(&mut self) -> Result<(), Error<SPI, CS>> {
262 self.write_enable()?;
263 let mut cmd_buf = [Opcode::ChipErase as u8];
264 self.command(&mut cmd_buf)?;
265 self.wait_done()?;
266 Ok(())
267 }
268}
269
270#[cfg(test)]
271mod tests {
272 use super::*;
273
274 #[test]
275 fn test_decode_jedec_id() {
276 let cypress_id_bytes = [0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0x7F, 0xC2, 0x22, 0x08];
277 let ident = Identification::from_jedec_id(&cypress_id_bytes);
278 assert_eq!(0xC2, ident.mfr_code());
279 assert_eq!(6, ident.continuation_count());
280 let device_id = ident.device_id();
281 assert_eq!(device_id[0], 0x22);
282 assert_eq!(device_id[1], 0x08);
283 }
284}