spi_memory/
series25.rs

1//! Driver for 25-series SPI Flash and EEPROM chips.
2
3use 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
10/// 3-Byte JEDEC manufacturer and device identification.
11pub struct Identification {
12    /// Data collected
13    /// - First byte is the manufacturer's ID code from eg JEDEC Publication No. 106AJ
14    /// - The trailing bytes are a manufacturer-specific device ID.
15    bytes: [u8; 3],
16
17    /// The number of continuations that precede the main manufacturer ID
18    continuations: u8,
19}
20
21impl Identification {
22    /// Build an Identification from JEDEC ID bytes.
23    pub fn from_jedec_id(buf: &[u8]) -> Identification {
24        // Example response for Cypress part FM25V02A:
25        // 7F 7F 7F 7F 7F 7F C2 22 08  (9 bytes)
26        // 0x7F is a "continuation code", not part of the core manufacturer ID
27        // 0xC2 is the company identifier for Cypress (Ramtron)
28
29        // Find the end of the continuation bytes (0x7F)
30        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    /// The JEDEC manufacturer code for this chip.
45    pub fn mfr_code(&self) -> u8 {
46        self.bytes[0]
47    }
48
49    /// The manufacturer-specific device ID for this chip.
50    pub fn device_id(&self) -> &[u8] {
51        self.bytes[1..].as_ref()
52    }
53
54    /// Number of continuation codes in this chip ID.
55    ///
56    /// For example the ARM Ltd identifier is `7F 7F 7F 7F 3B` (5 bytes), so
57    /// the continuation count is 4.
58    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)] // TODO support more features
72enum Opcode {
73    /// Read the 8-bit legacy device ID.
74    ReadDeviceId = 0xAB,
75    /// Read the 8-bit manufacturer and device IDs.
76    ReadMfDId = 0x90,
77    /// Read 16-bit manufacturer ID and 8-bit device ID.
78    ReadJedecId = 0x9F,
79    /// Set the write enable latch.
80    WriteEnable = 0x06,
81    /// Clear the write enable latch.
82    WriteDisable = 0x04,
83    /// Read the 8-bit status register.
84    ReadStatus = 0x05,
85    /// Write the 8-bit status register. Not all bits are writeable.
86    WriteStatus = 0x01,
87    Read = 0x03,
88    PageProg = 0x02, // directly writes to EEPROMs too
89    SectorErase = 0x20,
90    BlockErase = 0xD8,
91    ChipErase = 0xC7,
92}
93
94bitflags! {
95    /// Status register bits.
96    pub struct Status: u8 {
97        /// Erase or write in progress.
98        const BUSY = 1 << 0;
99        /// Status of the **W**rite **E**nable **L**atch.
100        const WEL = 1 << 1;
101        /// The 3 protection region bits.
102        const PROT = 0b00011100;
103        /// **S**tatus **R**egister **W**rite **D**isable bit.
104        const SRWD = 1 << 7;
105    }
106}
107
108/// Driver for 25-series SPI Flash chips.
109///
110/// # Type Parameters
111///
112/// * **`SPI`**: The SPI master to which the flash chip is attached.
113/// * **`CS`**: The **C**hip-**S**elect line attached to the `\CS`/`\CE` pin of
114///   the flash chip.
115#[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    /// Creates a new 25-series flash driver.
123    ///
124    /// # Parameters
125    ///
126    /// * **`spi`**: An SPI master. Must be configured to operate in the correct
127    ///   mode for the device.
128    /// * **`cs`**: The **C**hip-**S**elect Pin connected to the `\CS`/`\CE` pin
129    ///   of the flash chip. Will be driven low when accessing the device.
130    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        // Here we don't expect any writes to be in progress, and the latch must
136        // also be deasserted.
137        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        // If the SPI transfer fails, make sure to disable CS anyways
146        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    /// Reads the JEDEC manufacturer/device identification.
154    pub fn read_jedec_id(&mut self) -> Result<Identification, Error<SPI, CS>> {
155        // Optimistically read 12 bytes, even though some identifiers will be shorter
156        let mut buf: [u8; 12] = [0; 12];
157        buf[0] = Opcode::ReadJedecId as u8;
158        self.command(&mut buf)?;
159
160        // Skip buf[0] (SPI read response byte)
161        Ok(Identification::from_jedec_id(&buf[1..]))
162    }
163
164    /// Reads the status register.
165    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        // TODO: Consider changing this to a delay based pattern
180        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    /// Reads flash contents into `buf`, starting at `addr`.
187    ///
188    /// Note that `addr` is not fully decoded: Flash chips will typically only
189    /// look at the lowest `N` bits needed to encode their size, which means
190    /// that the contents are "mirrored" to addresses that are a multiple of the
191    /// flash size. Only 24 bits of `addr` are transferred to the device in any
192    /// case, limiting the maximum size of 25-series SPI flash chips to 16 MiB.
193    ///
194    /// # Parameters
195    ///
196    /// * `addr`: 24-bit address to start reading at.
197    /// * `buf`: Destination buffer to fill.
198    fn read(&mut self, addr: u32, buf: &mut [u8]) -> Result<(), Error<SPI, CS>> {
199        // TODO what happens if `buf` is empty?
200
201        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}