w25q/
series25.rs

1//! Driver for 25-series SPI Flash and EEPROM chips.
2
3use 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
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#[repr(u8)]
72#[allow(unused)] // TODO support more features
73enum Opcode {
74    /// Read the 8-bit legacy device ID.
75    ReadDeviceId = 0xAB,
76    /// Read the 8-bit manufacturer and device IDs.
77    ReadMfDId = 0x90,
78    /// Read 16-bit manufacturer ID and 8-bit device ID.
79    ReadJedecId = 0x9F,
80    /// Set the write enable latch.
81    WriteEnable = 0x06,
82    /// Clear the write enable latch.
83    WriteDisable = 0x04,
84    /// Read the 8-bit status register.
85    ReadStatus = 0x05,
86    /// Write the 8-bit status register. Not all bits are writeable.
87    WriteStatus = 0x01,
88    Read = 0x03,
89    PageProg = 0x02, // directly writes to EEPROMs too
90    SectorErase = 0x20,
91    BlockErase = 0xD8,
92    ChipErase = 0xC7,
93    PowerDown = 0xB9,
94}
95
96bitflags! {
97    /// Status register bits.
98    pub struct Status: u8 {
99        /// Erase or write in progress.
100        const BUSY = 1 << 0;
101        /// Status of the **W**rite **E**nable **L**atch.
102        const WEL = 1 << 1;
103        /// The 3 protection region bits.
104        const PROT = 0b00011100;
105        /// **S**tatus **R**egister **W**rite **D**isable bit.
106        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/// Driver for 25-series SPI Flash chips.
132///
133/// # Type Parameters
134///
135/// * **`SPI`**: The SPI master to which the flash chip is attached.
136/// * **`CS`**: The **C**hip-**S**elect line attached to the `\CS`/`\CE` pin of
137///   the flash chip.
138#[derive(Debug)]
139pub struct Flash<SPI, CS: OutputPin> {
140    spi: SPI,
141    cs: CS,
142}
143
144// for multiple SPI use: https://crates.io/crates/shared-bus-rtic
145
146impl<SPI: Transfer<u8>, CS: OutputPin> Flash<SPI, CS> {
147    /// Creates a new 25-series flash driver.
148    ///
149    /// # Parameters
150    ///
151    /// * **`spi`**: An SPI master. Must be configured to operate in the correct
152    ///   mode for the device.
153    /// * **`cs`**: The **C**hip-**S**elect Pin connected to the `\CS`/`\CE` pin
154    ///   of the flash chip. Will be driven low when accessing the device.
155    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        // Here we don't expect any writes to be in progress, and the latch must
162        // also be deasserted.
163        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        // If the SPI transfer fails, make sure to disable CS anyways
172        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    /// Reads the JEDEC manufacturer/device identification.
180    pub fn read_jedec_id(&mut self) -> Result<Identification, Error<SPI, CS>> {
181        // Optimistically read 12 bytes, even though some identifiers will be shorter
182        let mut buf: [u8; 12] = [0; 12];
183        buf[0] = Opcode::ReadJedecId as u8;
184        self.command(&mut buf)?;
185
186        // Skip buf[0] (SPI read response byte)
187        Ok(Identification::from_jedec_id(&buf[1..]))
188    }
189
190    /// Reads the status register.
191    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, // W25Q512
208            0x19 => 512,  // W25Q256
209            0x18 => 256,  // W25Q128
210            0x17 => 128,  // W25Q64
211            0x16 => 64,   // W25Q32
212            0x15 => 32,   // W25Q16
213            0x14 => 16,   // W25Q80
214            0x13 => 8,    // W25Q40
215            0x12 => 4,    // W25Q20
216            0x11 => 2,    // W25Q10
217            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        // TODO: Consider changing this to a delay based pattern
243        while self.read_status()?.contains(Status::BUSY) {}
244        Ok(())
245    }
246
247    /// Enters power down mode.
248    /// Datasheet, 8.2.35: Power-down:
249    /// Although  the  standby  current  during  normal  operation  is  relatively  low,  standby  current  can  be  further
250    /// reduced  with  the  Power-down  instruction.  The  lower  power  consumption  makes  the  Power-down
251    /// instruction especially useful for battery powered applications (See ICC1 and ICC2 in AC Characteristics).
252    /// The instruction is initiated by driving the /CS pin low and shifting the instruction code “B9h” as shown in
253    /// Figure 44.  
254    ///  
255    /// The /CS pin must be driven high after the eighth bit has been latched. If this is not done the Power-down
256    /// instruction will not be executed. After /CS is driven high, the power-down state will entered within the time
257    /// duration of tDP (See AC Characteristics). While in the power-down state only the Release Power-down /
258    /// Device ID (ABh) instruction, which restores the device to normal operation, will be recognized. All other
259    /// instructions  are  ignored.  This  includes  the  Read  Status  Register  instruction,  which  is  always  available
260    /// during normal operation. Ignoring all but one instruction makes the Power Down state a useful condition
261    /// for  securing maximum  write protection. The  device  always  powers-up  in the  normal  operation with  the
262    /// standby current of ICC1.   
263    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    /// Exits Power Down Mode
271    /// Datasheet, 8.2.36: Release Power-down:
272    /// The Release from Power-down /  Device ID instruction is  a multi-purpose instruction. It can be used to
273    /// release the device from the power-down state, or obtain the devices electronic identification (ID) number.   
274    /// To  release the device  from  the  power-down state,  the instruction  is  issued by driving the  /CS  pin low,
275    /// shifting the instruction code “ABh” and driving /CS high as shown in Figure 45. Release from power-down
276    /// will  take  the  time  duration  of  tRES1  (See  AC  Characteristics)  before  the  device  will  resume  normal
277    /// operation  and  other  instructions  are  accepted.  The  /CS  pin  must  remain  high  during  the  tRES1  time
278    /// duration.
279    ///
280    /// Note: must manually delay after running this, IOC
281    pub fn release_power_down<D: DelayUs<u8>>(
282        &mut self,
283        delay: &mut D,
284    ) -> Result<(), Error<SPI, CS>> {
285        // Same command as reading ID.. Wakes instead of reading ID if not followed by 3 dummy bytes.
286        let mut buf = [Opcode::ReadDeviceId as u8];
287        self.command(&mut buf)?;
288
289        delay.delay_us(6); // Table 9.7: AC Electrical Characteristics: tRES1 = max 3us.
290
291        Ok(())
292    }
293
294    /// Reads flash contents into `buf`, starting at `addr`.
295    ///
296    /// Note that `addr` is not fully decoded: Flash chips will typically only
297    /// look at the lowest `N` bits needed to encode their size, which means
298    /// that the contents are "mirrored" to addresses that are a multiple of the
299    /// flash size. Only 24 bits of `addr` are transferred to the device in any
300    /// case, limiting the maximum size of 25-series SPI flash chips to 16 MiB.
301    ///
302    /// # Parameters
303    ///
304    /// * `addr`: 24-bit address to start reading at.
305    /// * `buf`: Destination buffer to fill.
306    pub fn read(&mut self, addr: u32, buf: &mut [u8]) -> Result<(), Error<SPI, CS>> {
307        // TODO what happens if `buf` is empty?
308
309        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}